问题 为什么Task.Delay()允许无限延迟?


在我的应用程序冻结之后,我将原因跟踪到等待创建的任务的线程 Task.Delay() (要么 TaskEx.Delay() 在.NET 4.0中,它提供了一个计算 TimeSpan 由于一个错误,有时计算到一个 TimeSpan 用一个 TotalMilliseconds 小于或等于 -1 并且大于 -2 (即-10000到-19999个刻度之间的任何位置,包括在内)。

当你通过否定时,似乎 TimeSpan 那是 -2 毫秒或更低,该方法正确抛出一个 ArgumentOutOfRangeException,但是当您从上述范围中提供负时间间隔时,它会返回一个 Task 从未完成(通过设置基础 System.Threading.Timer 到了 dueTime -1表示无穷大)。这意味着在该任务上设置的任何延续都将永远不会执行,并且任何可怜的线程都会发生 .Wait() 在那 Task 将永远被封锁。

什么可能的用途可以 Task 永远不会完成的?有人会期望这样的回报值吗?不应该传递任何负值 .Delay(),包括该特殊范围内的值,抛出一个 ArgumentOutOfRangeException


5137
2018-05-24 16:13


起源

MSDN doc在允许-1时非常明确,所以它似乎表现正常。不确定该重载的用例,但它可能是一种等待'just'取消的方法,带有取消令牌的重载。 - James Manning
@James:在允许-1时没有明确说明,它明确禁止低于-1的值。它甚至没有说明如果你传递-1会发生什么,不像文档 System.Threading.Timer。几乎看起来像记录的异常列表是从源代码自动生成的。如果你正在等待'只是'取消,为什么甚至打电话给 Task.Delay()? - Allon Guralnek
如果您认为它已损坏,请在连接上提交错误。一个说“低于-1无效”的文件明确(对我来说)-1是有效的。如果意图为-1,因为无效“低于0无效”将更容易编写。由于doc和代码都允许-1,我认为这是By Design,但是可以自由地在连接上提交错误(更有可能由BCL团队处理而不是随机SO线程,我想:) - James Manning
@James:我在SO上发布它的原因是因为我不确定它是一个bug。我正在寻找这个API设计的解释,假设它是故意的。例如,svick的用例是有道理的。 - Allon Guralnek


答案:


Timeout.Infinite 当你想无限期地等待一个长时间运行的任务时,或者-1非常有用,这个任务将花费不确定的时间来完成,但最终会完成。

Win32 API还使用常量INFINITE = -1进行无限超时。

您通常不希望在UI线程中使用它,因为它可能会冻结UI(这似乎是您的问题)。但是在工作线程中存在有效的用例 - 例如阻止等待来自客户端的连接的服务器。


8
2018-05-24 17:26



无限超时很有用。无限延迟不是(因此您的第一段不适用)。世界上你想永远拖延什么?你也可以不执行它。它对我的实现细节没有任何意义 -1 将从中传播 System.Threading.Timer (或Win32计时器)最多 Task.Delay() 方法。这与微软推动开发人员进入“成功陷阱”的设计原则背道而驰,除非有一些我不了解的用例。 - Allon Guralnek
另外,我没有在UI线程中使用它。这是一个Windows服务,当被要求停止时,它必须通过调用a来执行关闭例程 TAP基于该方法可能需要很长时间才能运行,因此它还会创建一个执行强制关闭的延迟任务。然后它执行一个 .WaitAny() 在这两个任务上,由于原始任务花费了很长时间并且延迟任务永远不会完成(而不是抛出异常),服务似乎挂起了。 - Allon Guralnek
如何阻止等待来自客户端的连接的服务器将使用 Task 永远不会完成?你能举个例子吗? - Allon Guralnek
@AllonGuralnek,你可以做点什么 await Task.WhenAny(mainTask, Task.Delay(timeout))。如果你设置 timeout 到-1,然后它永远不会超时。 - svick
@Joe,只有你这样才会冻结用户界面 Wait() 在上面。如果你使用它 await,它在UI线程上也很好。 - svick


在模拟场景中,我想确保我的Task.WhenAny()块中的代码正在处理正在等待的任务之一,我可以模拟其他任务并使用无限延迟来确保Task.WhenAny正在处理我没有嘲笑任务无限延迟。


2
2017-11-25 23:32



有趣的用例,虽然我认为使用它会更明确和清晰 new TaskCompletionSource().Task 提供永无止境的任务。 - Allon Guralnek