【问题标题】:Why does Task.Delay() allow an infinite delay?为什么 Task.Delay() 允许无限延迟?
【发布时间】:2012-05-31 06:17:43
【问题描述】:

在我的应用程序冻结后,我追踪了一个线程等待由Task.Delay()(或.NET 4.0 中的TaskEx.Delay())创建的任务的原因,它提供了一个计算的TimeSpan,由于一个错误,有时被计算为TimeSpan,其中TotalMilliseconds 小于或等于-1 且大于-2(即介于-10000 到-19999 滴答之间的任意值,包括)。

看来,当您传递一个负数 TimeSpan(即 -2 毫秒或更低)时,该方法会正确地抛出一个 ArgumentOutOfRangeException,但是当您提供上述范围内的负 TimeSpan 时,它会返回一个 @987654331 @ 永远不会完成(通过将基础 System.Threading.Timer 设置为 -1 的 dueTime 表示无穷大)。这意味着在该任务上设置的任何延续都将永远不会执行,并且在该Task 上发生在.Wait() 上的任何不良线程将永远被阻止。

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

【问题讨论】:

  • MSDN 文档非常明确地允许 -1,因此它的行为似乎正确。不确定该重载的用例,但它可能是一种等待“只是”取消带有取消令牌的重载的方法。
  • @James:允许 -1 并不明确,但不允许低于 -1 的值是明确的。与System.Threading.Timer 的文档不同,它甚至没有说明如果通过 -1 会发生什么。几乎看起来记录的异常列表是从源代码自动生成的。如果您正在等待“只是”取消,为什么还要打电话给Task.Delay()
  • 如果您认为它已损坏,请在连接时提交错误。一个说“低于 -1 无效”的文档(对我来说)明确表示 -1 是有效的。如果意图是 -1 无效,“低于 0 无效”会更容易编写。由于文档和代码都允许-1,我认为这是设计使然,但请随时在连接上提交错误(我认为 BCL 团队更有可能处理而不是随机 SO 线程 :)
  • @James:我在 SO 上发布它的原因是因为我不确定这是一个错误。我正在寻找这个 API 设计的解释,假设它是故意的。例如,svick 的用例是有意义的。

标签: .net .net-4.0 task-parallel-library .net-4.5 async-await


【解决方案1】:

当我希望控制台应用程序不终止时,我正在使用它。 例如,当我为 Azure 编写 Web 作业 时,我需要一个永不终止的程序。这是模板:

public async Task Main(string args[])
{
    var timer = new Timer();
    timer.Interval = 6000;
    timer.Elapsed += (sender, args) =>
    {
        // Do something repeatedly, each 1 minute.
    };
    timer.Start();

    // Now what to do to stop the application from terminating!?
    // That's the magic:
    await Task.Delay(-1);
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 有趣的用例,虽然我认为使用new TaskCompletionSource().Task 来提供永无止境的任务会更加明确和清晰。
    • @AllonGuralnek 创建永无止境任务的另一种方法是使用带有空委托的Task 构造函数:new Task(() => {})。该任务永远不会完成,因为它永远不会开始。
    • @TheodorZoulias 直到有人偷偷打电话给.Start() ?
    【解决方案3】:

    Timeout.Infinite 或 -1 在您想无限期地等待一个长时间运行的任务时很有用,该任务需要不确定的时间才能完成,但最终会完成。

    Win32 API 还使用常量 INFINITE = -1 来表示无限超时。

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

    【讨论】:

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