我应该总是使用Task.Delay而不是Thread.Sleep吗?

6
最近我看到了几篇建议,认为在生产代码中永远不应该使用Thread.Sleep(最近在这个SO问题中也有类似的说法)。其中许多人主张使用Task.Delay。我找到的大部分解释都是以UI应用程序为例的,因为Task.Delay的优点很明显(不会阻塞UI)。
在我的情况下,我正在使用Thread.Sleep来等待轮询WCF服务的特定条件,像这样:
DateTime end = DateTime.UtcNow + TimeSpan.FromMinutes(2);
while (DateTime.UtcNow < end)
{
    if (ExternalServiceIsReady() == true)
    {
        return true;
    }
    Thread.Sleep(1000);
}

在这种情况下,Task.Delay 的以下潜在优势似乎并不适用:
  • 相对于典型定时器分辨率约为15ms而言,睡眠时间相当长,因此使用Task.Delay增加的精度微不足道。
  • 该进程是单线程(非UI)且必须阻塞直到条件为真,因此在此处使用await没有任何优势。
  • 不需要取消延迟的能力。
在这种情况下,是否适合使用Thread.Sleep?如果将我的睡眠行替换为Task.Delay(1000).Wait(),有什么优点(如果有)?

2
ExternalServiceIsReady 是做什么的?也许可以完全避免使用 Sleep/Delay。 - EZI
2
有没有使用事件的选项C?这可能比轮询方法更合适。 - Jeroen Vannevel
1
选项 D 是更优秀的解决方案,一个计时器。 - Hans Passant
1
没有循环,没有线程,没有休眠。 - Hans Passant
1
@nintendojunkie - 请考虑时钟可能会被用户、NTP等修改。 - Matt Johnson-Pint
显示剩余6条评论
1个回答

7
Task.Delay(1000).Wait();中替换Thread.Sleep(1000);没有任何优势。如果您想同步等待,只需使用Thread.Sleep即可。
如果您真的只有一个线程并打算保持这种状态,那么可以使用Thread.Sleep。但是,在大多数情况下,我仍然会使用Task.Delay,因为它是更可取的,所以这是一个好的模式。当您无法再使用async时,我只会在非常顶部阻塞,即使这样,我也建议使用某种AsyncContext
您还可以直接使用System.Threading.Timer*而不是Task.Delay,但是请记住,定时器每个间隔都会执行,并且不会等待实际操作完成,因此,如果ExternalServiceIsReady需要的时间超过了间隔,您可能会同时调用该服务多次。
更好的解决方案是用异步操作替换对外部服务的轮询,这样服务就可以在准备好时通知您,而不是您每秒都要问一次(这并不总是可能的,这取决于服务):
await ExternalServiceIsReadyAsync();

* Task.Delay 内部使用了一个分辨率约为15毫秒的 System.Threading.Timer


2
更好的方法是,如果ExternalServiceIsReady可以抛出一个事件,那么就不需要使用sleep/wait了。 - EZI
@EZI 异步操作意味着在完成时会通知您。无论是通过完成一个“任务”、调用回调函数还是触发事件,都没有关系。这些方法都是相同的,可以转换为其他方法。 - i3arnon
@EZI 将同步阻塞循环替换为异步可等待调用,意味着少了一个线程(及其 CPU 周期)的浪费。 - i3arnon
1
@i3arnon,回应您的最后一条评论:Thread.Sleep 不会浪费任何 CPU 循环。它只会占用一个可用资源(线程),但不会消耗任何 CPU 循环。您所指的是 while 循环和那些条件吗? - Sriram Sakthivel
2
另一个小问题。Task.Delay会创建一个Task(内存分配),task.Wait()将会旋转一段时间,然后在那个点之后分配一个内核事件(内核资源和用户->内核转换),所有这些都需要CPU周期 :) - Sriram Sakthivel
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接