在对验证并发性的测试进行故障排除时,我发现我们的一些构建计算机(在 VM 中运行(始终报告任务没有等待完整的Task.Delay
间隔。为了确认,我写了一个测试,它创建了一个直方图,显示从请求的延迟开始花费了多少毫秒。
在我的机器上,结果如您所料:
interval ms => number of items completing in that interval
--
0 - 4 ms => 13 # 13 iterations completed within 4 ms of the requested delay
5 - 9 ms => 194
10 - 14 ms => 714
15 - 19 ms => 61
20 - 24 ms => 12
25 - 29 ms => 3
40 - 44 ms => 2
45 - 49 ms => 1
但在构建计算机上,测试报告在请求的延迟完成之前已完成的所有任务:
-10 - -6 ms => 999
-5 - -1 ms => 1
这是测试/直方图生成器:
[Test]
[Timeout(60_000)]
public async Task TaskDelayAccuracyCheck()
{
var results = new List<long>();
for (var i = 0; i<1000; ++i)
{
var sw = new Stopwatch();
sw.Start();
await Task.Delay(20);
sw.Stop();
results.Add((sw.ElapsedTicks - 20*10_000)/10_000);
}
var histo = results.GroupBy(t => t / 5).OrderBy(x => x.Key);
foreach (var group in histo)
{
Console.WriteLine($"{group.Key*5} - {(group.Key+1)*5 - 1} ms => {group.Count()}");
}
Assert.Multiple(() =>
{
foreach (var group in histo)
{
Assert.That(group.Key, Is.GreaterThanOrEqualTo(0));
}
});
}
Task.Delay文档说(着重号是加的(:
此方法取决于系统时钟。这意味着,如果延迟参数小于系统时钟的分辨率(在 Windows 系统上约为 15 毫秒(,则时间延迟将大致等于系统时钟的分辨率。
在本例中,我已确保延迟不小于上述 15 毫秒,并且我使用了Stopwatch
类来确保时序尽可能精确。此外,Stopwatch.IsHighResolution返回true
,所以我应该能够信任时间。
我一直在假设延迟始终至少是请求的时间,但根据系统时钟的分辨率可能会更长。我是否应该推断延迟将始终在(系统时钟分辨率(或大约 15 毫秒的 Windows 内?或者,是不是发生了其他事情?
我认为你的测量不正确。
您假设每毫秒始终有 10,000 个刻度。但情况可能并非总是如此。您可以查看Stopwatch.Frequency
以查看它在当前系统上使用的每秒刻度数。该值是在第一次使用Stopwatch
时根据对本机 WindowsQueryPerformanceFrequency
函数的调用设置的。
除以 1000 得到每毫秒的刻度。
var ticksPerMillisecond = Stopwatch.Frequency / 1000;
但是,您可以更轻松地使用ElapsedMilliseconds
属性,该属性可为您准确地将刻度转换为毫秒。
results.Add(sw.ElapsedMilliseconds - 20);