c# - 我怎样才能使这个 DateTime.Now.ToFileTimeUtc() 成为线程安全的?
问题描述
我想使用 DateTime.Now.ToFileTimeUtc() 生成文件名,但是使用多线程我为多个线程获得相同的文件名,这导致写入文件的 I/O 错误。我想让每个线程都有一个单独的文件。
我如何在 C# 中使用 DateTime.Now.ToFileTimeUtc() 获得不同的文件名?
解决方案
如果您在紧密循环中打印出 的值DateTime.Now.ToFileTimeUtc()
,您将看到如下结果:
132453421456289289
132453421456289289
132453421456305151
132453421456312499
132453421456312499
132453421456312499
132453421456322499
132453421456322499
132453421456332746
132453421456342443
132453421456342443
132453421456352425
132453421456352425
132453421456362391
这告诉我们两件事:
- 返回值有时会重复。
- 值之间的最小间隔大约超过 1,000(以 100ns 为单位测量)。
由于时间的实际精度远小于时间的精度单位,我们可以安全地解决这个问题,如下所示:
首先,创建一个调整整数,初始化为零。然后:
- 将文件时间值转换为 10,000 的倍数。这不会失去显着的精度。
- 如果要返回的文件时间与之前的时间相同,则增加调整。
- 否则,将调整设置回零。
例如:
public static class UniqueFileTime
{
public static long Generate()
{
long next = 10_000 * (DateTime.Now.ToFileTimeUtc() / 10_000);
lock (_lock)
{
next = Math.Max(next, _last);
if (next == _last)
{
++_adj;
if (_adj == 10_000) // Broken!
throw new InvalidOperationException("UniqueFileTime.Generate() called too often.");
}
else
{
_adj = 0;
_last = next;
}
return next + _adj;
}
}
static long _last;
static int _adj;
static readonly object _lock = new object();
}
这种实现具有最小的开销,并且锁应该只保持极短的时间。
这确实意味着文件时间的精度是最接近的 10,000 个 100 纳秒单位(因为ToFileTimeUtc()
返回一个以 100 纳秒为单位的值)。这仍然是 1 毫秒的精度 - 对于这些目的的文件时间来说已经足够了。
这是一个小测试程序来强调它:
static class Program
{
public static void Main()
{
var results = new List<List<long>>(1000);
for (int i = 0; i < 8; ++i)
results.Add(new List<long>());
Parallel.Invoke(
() => getTimes(results[0]),
() => getTimes(results[1]),
() => getTimes(results[2]),
() => getTimes(results[3]),
() => getTimes(results[4]),
() => getTimes(results[5]),
() => getTimes(results[6]),
() => getTimes(results[7])
);
foreach (var time in results.SelectMany(r => r))
{
Console.WriteLine(time);
}
int distinctCount = results.SelectMany(r => r).Distinct().Count();
if (distinctCount != 8000)
Console.WriteLine("FAILED - Distinct should be 8000, but was " + distinctCount);
}
static void getTimes(List<long> times)
{
for (int i = 0; i < 1000; ++i)
times.Add(UniqueFileTime.Generate());
}
}
通过创建 8 个线程并在每个线程的紧密循环中获得独特的时间,这确实给它带来了压力。
推荐阅读
- python - Python Selenium - 下拉菜单
- azure - 如何在调用后端之前在 Azure API 管理中添加自定义标头
- graphics - 控制交换链当前调用频率的正确方法是什么?
- .net - 在 Microsoft Teams 上共享主窗口时,透明子窗口呈现为黑色
- python - Python if-else - 条件错误处理
- arrays - 打字稿说即使数组保证包含元素,array.pop() 也可能返回 undefined
- java - Spring Boot - CORS 过滤器适用于 GET,但不适用于其他 HTTP 动词
- node.js - 我的请求正文没有(?)进入我的应用程序
- .net - 如何为不同的环境发布 .NET Core + Angular 单页应用程序?
- laravel - 如何在 Dusk 测试中选择多个复选框