我在.NET Framework和.NET Core中都观察到Task.Delay()似乎比它应该完成 早期 。通常情况下,未成年人是10岁以上的用户,但在极少数情况下,它可能会高达几毫秒。考虑一下这个程序:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("TaskDelayTest .NET Framework");
while (true)
{
DateTime now = DateTime.UtcNow;
TimeSpan wait = TimeSpan.FromMilliseconds(1000);
DateTime then = now + wait;
Task delay = Task.Delay(wait);
delay.ContinueWith(Execute, then);
Thread.Sleep(100);
}
}
static void Execute(Task delay, object arg)
{
DateTime later = DateTime.UtcNow;
DateTime then = (DateTime)arg;
if (later < then)
{
Console.WriteLine("Early execute!!!! {0:n0} ns", (then.Ticks - later.Ticks) * 100);
}
}
}
我希望&#34;早期执行&#34;因为Task.Delay应该等待至少与延迟参数一样长,否则不会打印该行。但是,这不是我观察到的。如果你允许程序运行的时间足够长,它会打印出来#34;提前执行&#34;。我在这里误解了这个规范吗?
TaskDelayTest .NET Core
Early execute!!!! 199,800 ns
Early execute!!!! 22,200 ns
Early execute!!!! 353,300 ns
Early execute!!!! 571,200 ns
Early execute!!!! 90,700 ns
Early execute!!!! 85,600 ns
Early execute!!!! 9,300 ns
Early execute!!!! 540,600 ns
Early execute!!!! 141,200 ns
Early execute!!!! 107,800 ns
Early execute!!!! 397,200 ns
Early execute!!!! 297,000 ns
答案 0 :(得分:9)
您所看到的唤醒时间的变化完全在Windows上的预期性能范围内。在1000毫秒的等待中,您偶尔会看到早期的唤醒,总是大约1毫秒或更短。
在查看这样的问题时,查看构建这些更高抽象的低级操作系统功能的文档非常有用且信息丰富。在Task.Delay()
的情况下,这基于维护定时器事件队列的定时器线程,其中Thread.SleepEx()
用于在定时器事件之间引起延迟。在the documentation for that function中,它显示为:
如果dwMilliseconds小于系统时钟的分辨率,则线程可能会睡眠时间少于指定的时间长度。如果dwMilliseconds大于一个tick但小于2,则等待可以是一到两个滴答之间的任何位置,依此类推。
换句话说,无论您指定的等待时间是多少,如果它不是系统时钟分辨率的偶数倍,您可以比时间更早地获得唤醒你指定的,在系统时钟分辨率的范围内。
几乎任何依赖于线程调度程序的计时机制都会有相同的限制,包括Task.Delay()
。