为什么Thread.Sleep如此有害

我经常看到它提到了Thread.Sleep(); 不应该使用,但我不明白为什么这样。 如果Thread.Sleep(); 会造成麻烦,有没有其他的解决方案可以保证安全?

例如。

 while(true) { doSomework(); i++; Thread.Sleep(5000); } 

另一个是:

 while (true) { string[] images = Directory.GetFiles(@"C:\Dir", "*.png"); foreach (string image in images) { this.Invoke(() => this.Enabled = true); pictureBox1.Image = new Bitmap(image); Thread.Sleep(1000); } } 

调用Thread.Sleep的问题在这里解释得非常简洁 :

Thread.Sleep有其用处:在MTA线程上测试/调试时模拟冗长的操作。 在.NET中,没有其他理由使用它。

Thread.Sleep(n)意味着至少在n毫秒内可以发生的次数(或者线程量)的数量来阻塞当前线程。 时间片的长度在Windows和不同处理器的不同版本/类型上是不同的,一般在15到30毫秒之间。 这意味着线程几乎可以保证阻塞超过n毫秒。 你的线程在n毫秒后重新唤醒的可能性大概是不可能的。 所以, Thread.Sleep对于时间是没有意义的

线程是一个有限的资源,他们需要大约20万个周期来创建和大约10万个周期来销毁。 默认情况下,它们为其堆栈保留1兆字节的虚拟内存,并为每个上下文切换使用2,000-8,000个周期。 这使得任何等待的线程都是巨大的浪费。

首选解决方案: WaitHandles

最大的错误是使用Thread.Sleep while-construct( 演示和答案 , 漂亮的博客条目 )

编辑:
我想提高我的答案:

我们有两种不同的用例:

  1. 我们正在等待,因为我们知道应该继续的特定时间段(使用Thread.SleepSystem.Threading.Timer或类似的方法)

  2. 我们正在等待,因为一些条件改变了一段时间…关键字是一段时间 ! 如果条件检查在我们的代码域,我们应该使用WaitHandles – 否则外部组件应该提供某种钩子…如果它不是它的设计是坏的!

我的答案主要包括用例2

SCENARIO 1 – 等待异步任务完成:我同意WaitHandle / Auto | ManualResetEvent应该在线程正在等待另一个线程上的任务完成的场景中使用。

情景2-时间while循环:然而,作为一个粗略的定时机制(while + Thread.Sleep)对于99%的应用程序来说是非常好的,它不需要知道阻塞的Thread 究竟应该“醒来”的确切时间。创建线程的200k个周期也是无效的 – 定时循环线程需要创建,而200k个周期只是另一个大数字(告诉我打开一个文件/套接字/数据库调用有多少个周期?)。

所以,如果+ Thread.Sleep同时工作,为什么复杂的东西? 只有语法律师才会实用

我想从编码政治的角度来回答这个问题,这对任何人都可能有帮助,也可能没有帮助。 但是,特别是在处理针对9-5公司程序员的工具时,编写文档的人倾向于使用“不应该”和“从不”这样的词来表示“除非你真的了解你为什么“。

我在C#世界的其他一些最喜欢的是,他们告诉你“永远不要调用锁(这个)”或“永远不要调用GC.Collect()”。 这两个是在许多博客和官方文件强制宣布,国际海事组织完整的错误信息。 在某种程度上,这种错误信息是为了达到目的,因为它使初学者远离做不了解的事情,然后再充分研究替代方案,但同时又很难通过搜索引擎找到真正的信息似乎指向文章,告诉你不要做什么,而不回答“为什么不?”的问题。

在政治上,归结为人们认为“好设计”或“坏设计”。 官方文档不应该指定我的应用程序的设计。 如果确实存在一个技术上的原因,那么你不应该叫sleep(),那么IMO文档就应该说明在特定情况下调用它是完全可以的,但也许会提供一些替代解决方案,这些解决方案是独立的或者更适合其他的场景。

显然,在很多情况下,如果在真实时间条件下明确定义了最终期限,那么调用“sleep()”是有用的,但是,在开始投入睡眠之前,还有更复杂的系统需要等待并发出信号, )添加到代码中,并在代码中抛出不必要的sleep()语句通常被认为是初学者的策略。

这是你的例子中的1).spinning和2).polling循环 ,而不是Thread.Sleep()部分。 我认为Thread.Sleep()通常被添加来轻松地改进正在旋转或在轮询循环中的代码,所以它只是与“坏”代码关联。

另外人们做这样的东西:

 while(inWait)Thread.Sleep(5000); 

不能以线程安全的方式访问变量inWait,这也会导致问题。

程序员希望看到的是由Events和Signaling和Locking结构控制的线程,当你这样做的时候,你不需要Thread.Sleep(),而且线程安全变量访问的问题也被消除了。 作为一个例子,你可以创建一个与FileSystemWatcher类关联的事件处理程序,并使用一个事件触发你的第二个例子而不是循环?

正如Andreas N.提到的, 通过Joe Albahari阅读C#中的Threading ,它真的非常好。

睡眠用于您无法控制的独立程序有时可能使用常用资源(例如文件),程序在运行时需要访问以及这些资源正在被这些资源使用时需要访问的情况其他程序被阻止使用它。 在这种情况下,在代码中访问资源的位置,您将资源的访问权限放在try-catch中(以便在无法访问资源时捕获异常),并将其放在while循环中。 如果资源是免费的,睡眠永远不会被调用。 但是,如果资源被阻塞,那么你会休息适当的时间,并尝试再次访问资源(这就是为什么你在循环)。 但是,请记住,您必须在循环中加入某种限制器,所以它不是一个潜在的无限循环。 您可以将您的限制条件设置为N次尝试(这是我通常使用的),或者检查系统时钟,添加固定时间以获得时间限制,如果达到时间限制,则退出尝试访问。

我在这里同意很多,但我也认为这取决于。

最近我做了这个代码:

 private void animate(FlowLayoutPanel element, int start, int end) { bool asc = end > start; element.Show(); while (start != end) { start += asc ? 1 : -1; element.Height = start; Thread.Sleep(1); } if (!asc) { element.Hide(); } element.Focus(); } 

这是一个简单的动画函数,我使用了Thread.Sleep

我的结论是,如果能做到这一点,就使用它。

对于那些在SCENARIO 2中没有看到反对使用Thread.Sleep的有效论证的人,确实有一个应用程序出口被while循环所占据(SCENARIO 1/3只是简单的愚蠢,所以不值得更多提)

许多人假装在知道尖叫的Thread.Sleep是邪恶的没有提到我们这些人要求一个实际的理由不使用它的一个有效的理由 – 但在这里,是由于皮特 – Thread.Sleep是邪恶的 (可以很容易地用一个定时器/处理程序来避免)

  static void Main(string[] args) { Thread t = new Thread(new ThreadStart(ThreadFunc)); t.Start(); Console.WriteLine("Hit any key to exit."); Console.ReadLine(); Console.WriteLine("App exiting"); return; } static void ThreadFunc() { int i=0; try { while (true) { Console.WriteLine(Thread.CurrentThread.ThreadState.ToString() + " " + i); Thread.Sleep(1000 * 10); i++; } } finally { Console.WriteLine("Exiting while loop"); } return; }