等待Task.Delay超过15毫秒的等待时间不够长

时间:2019-12-12 17:26:06

标签: c# async-await clock system-clock

在对验证并发性的测试进行故障排除时,我发现我们的某些构建计算机(在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 documentation说(强调):

  

此方法取决于系统时钟。这意味着,如果delay参数小于系统时钟的分辨率,则时间延迟将大致等于系统时钟的分辨率,在Windows系统上约为15毫秒。

在这种情况下,我确保延迟不小于上述提到的15毫秒,并且我使用Stopwatch类来确保计时精确尽可能。另外,Stopwatch.IsHighResolution返回true,所以我应该可以相信时间安排。

我一直在假设延迟将始终至少为请求的时间,但是可能会因系统时钟的分辨率而变长。我是否应该推断出延迟将始终在(系统时钟分辨率)范围内,或者在Windows上大约为15ms?或者,还有其他事情吗?

1 个答案:

答案 0 :(得分:4)

我认为您的测量方法不正确。

您假设每毫秒始终存在10,000个滴答声。但这并非总是如此。您可以查看Stopwatch.Frequency来查看它在当前系统上使用的每的滴答声。基于对本地Windows set the first time Stopwatch is used函数的调用,该值为QueryPerformanceFrequency

除以1000可得到每毫秒的滴答声。

var ticksPerMillisecond = Stopwatch.Frequency / 1000;

但是您甚至可以轻松使用ElapsedMilliseconds属性,为您准确地converts the ticks to milliseconds

results.Add(sw.ElapsedMilliseconds - 20);
相关问题