什么情况下使用Thread.Sleep()是明智的?

26

我经常看到人们使用Thread.Sleep()来创建处理延迟或类似的操作,但是人们总是因为这种用法而受到嘲笑。

什么情况下使用Thread.Sleep()是明智/必需的?


为什么人们会在没有任何解释的情况下对这个问题进行负面评价? - ediblecode
1
@ErickRobertson,你是在说应该创建两个问题吗?对我来说似乎有点微不足道。 - ediblecode
6
很惊奇地看到有很多人认为Sleep()方法是糟糕的设计。就像其他API一样,Sleep()方法是为特定目的而设计的。当然,如果滥用这个目的会导致糟糕的设计,但是自动假设所有Sleep()方法都是糟糕的本身就是错误的! - adelphus
1
在大多数情况下,它是在糟糕的设计中使用的。我已经看过它数百次了。我无法想到一个需要使用它的情况。 - BlueM
1
“这在现实世界中很少需要做的事情”,真的吗?你从未读过任何规范,上面写着“等待至少十秒钟再继续”吗? - Martin James
显示剩余3条评论
9个回答

31

当你需要在后台线程中进行延迟时,应该调用Thread.sleep()

不要在同步过程中调用它(它无法实现同步),不要在循环中等待某些内容时调用它(这样会很慢),也不要在UI线程上调用它(这样会导致程序冻结)。


14
补充一点:不要试图使用Thread.Sleep来构建一个准确的定时器。 - Brian Rasmussen
我在后台线程上使用Thread.Sleep,同时对一组文件或网络套接字进行长时间处理(并在线程完成时进行回调)。这是使用Thread.Sleep的好方法还是有更好的机制来实现这一点? - Matthew
@Matthew 听起来你可能在使用两个线程来完成一个线程的工作。睡眠线程除了定期唤醒并检查工作线程是否仍在工作之外,还有其他的事情要做吗?工作线程完成后,它不能自己调用回调函数吗? - Sean U
3
+1 不强调现在有点让人厌烦的“睡眠总是不好”的口号。我相信许多开发人员已经采纳了这一观点,现在正在尝试调试复杂的异步、计时器和其他事件驱动的状态机,而不是简单的过程式代码,其中包含使用 sleep() 进行等待,并将其运行在一个线程中。 - Martin James
3
@Matthew - 听起来像是“误用”类别,恐怕是这样的:(常见的套接字操作,如accept/read/write不需要轮询。 - Martin James
显示剩余2条评论

5
当你需要在一些临时或测试代码中引入暂停时,Thread.Sleep()是可以的。
在生产代码中,最好尝试找到另一个选项。例如,如果您想要按间隔执行某些操作,请使用众多现有的计时器类之一。如果您想在输入队列为空时暂停(且在.NET上),请考虑改用Monitor.Wait()和Monitor.Pulse()。
更多关于Sleep()缺点的解释(再次强调,.NET为中心)可以在本文中找到:Thread.Sleep is a sign of a poorly designed program.

2
+1 它有很多非生产代码用途。当开发/演示多线程框架时,它也很有用,可以展示长时间运行的任务,知道它只是真实工作的替代品。 - Servy
我之前读过那篇链接的文章。我刚刚又读了一遍,很糟糕。第二次读起来更糟糕了。 - Martin James

5

有很少的情况我认为使用Thread.Sleep是可以接受的。在我的想法中,最终归结于以下条件 - 其他人可能会在这些不适用的情况下发表意见,但作为一个经验法则,以下所有条件都需要成立,我才会在生产环境(即非微不足道或测试代码)中使用Thread.Sleep:

  1. 您正在等待某个资源
  2. 所涉及的资源没有提供适当的主动通知准备就绪的机制(如WaitHandle或其他机制)
  3. 您无法通过其他方式修改所涉及的资源来实现此目的
  4. 您已经测量并了解需要等待足够长的时间,以使SpinWait不合适,并且通过离开上下文获得更好的性能。

3

当编写测试代码时,如果您想查看某个函数如何处理被随机调用的多个线程。

此外,如果您想模拟延迟进行测试。比如说您想测试一个进度条。


1

它可以用来强制进行上下文切换(参数为1),或者让出CPU给优先级更高的线程(参数为0)...但这很少需要。


0
如果你需要使用 Thread.Sleep,可能意味着你的设计有问题。最好使用同步机制,如 AutoResetEvent 或 ManualResetEvent,并等待事件发生。我经常看到使用 Thread.Sleep 进行轮询,但如果可能的话,最好尝试使用事件。

1
这个回答对问题没有任何答案。 - ediblecode
我想告诉你,你永远不应该使用Thread.Sleep,最好使用事件机制。这是我的回答,也许不够清晰。 - BlueM
正如在http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx中的一个答案所提到的那样,它确实是糟糕设计的标志。 ".NET中没有其他使用它的理由。" - BlueM

0

使用sleep的投诉通常与有效(或者说无效)的多线程设计有关。多线程是一个庞大的主题,我建议您阅读一本好书(《Java并发编程实战》by Goetz)。

如果您想要延迟处理代码一段固定的时间,那么sleep()是个不错的选择(然而,Timer类是实现周期性行为的好方法)。


1
计时器迫使开发人员开发状态机来实现过程规范。如果一个操作由一系列简单阶段的序列定义,通常由长时间间隔分隔,那么函数调用和sleep()调用将直接映射到此。而异步状态机则不会这样做。 - Martin James
1
使用sleep的投诉通常与有效(或者说无效)的多线程设计有关。这是非常真实的,但可悲的是,在一些人的心目中,这被翻译成了“Sleep()是一种反模式”。每年,消防车、救护车和警车都会造成数百起交通事故 - 因此它们应该全部被禁止。 - Martin James

-1

每当您需要在操作中进行长时间暂停时,请使用它。如果规范说“现在等待至少10秒钟才能继续”,那么请调用sleep(10000)。还有一种选择-您可以将代码重写为状态引擎,以便控制可以被放弃,直到定时器事件被触发。表驱动状态机非常灵活,允许完全异步操作。生成的“代码”可能不会以任何方式类似于要求规范,几乎不可能理解正在发生什么,难以调试,并且是修改、维护和/或增强的噩梦,但您将能够避免那个讨厌的反模式“Sleep(10000)”调用。

正如其他人发布的那样,请勿将其用于线程间通信!所有多任务操作系统都有大量更有效的同步机制。


-2
如果你想让一个线程停止某些操作,有时候当你使用无限循环时,你不希望循环一直运行下去,因为这会占用大量的CPU资源。在循环中添加一个thread.sleep()方法可以让循环“休息一下”,给CPU一些时间来休息。^^

如果你曾经想要编写这样的程序,难道不会直接使用计时器吗? - ediblecode
1
如何使用计时器替换sleep()调用?假设您在第三方脚本解释器的“OnProcessLine”事件中,深入到运行不同脚本的多个线程堆栈的未知深度,并且您希望暂停10ms。如何使用计时器实现这一点? - Martin James

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