C# 为什么 Thread.Sleep 如此有害
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8815895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is Thread.Sleep so harmful
提问by Burimi
I often see it mentioned that Thread.Sleep();should not be used, but I can't understand why this is so. If Thread.Sleep();can cause trouble, are there any alternative solutions with the same result that would be safe?
我经常看到它提到Thread.Sleep();不应该使用,但我不明白为什么会这样。如果Thread.Sleep();会引起麻烦,是否有其他具有相同结果的替代解决方案是安全的?
eg.
例如。
while(true)
{
doSomework();
i++;
Thread.Sleep(5000);
}
another one is:
另一个是:
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);
}
}
采纳答案by Andreas Niedermair
The problems with calling Thread.Sleepare explained quite succinctly here:
调用的问题在这里Thread.Sleep得到了非常简洁的解释:
Thread.Sleephas its use: simulating lengthy operations while testing/debugging on an MTA thread. In .NET there's no other reason to use it.
Thread.Sleep(n)means block the current thread for at least the number of timeslices (or thread quantums) that can occur withinnmilliseconds. The length of a timeslice is different on different versions/types of Windows and different processors and generally ranges from 15 to 30 milliseconds. This means the thread is almost guaranteed to block for more thannmilliseconds. The likelihood that your thread will re-awaken exactly afternmilliseconds is about as impossible as impossible can be. So,Thread.Sleepis pointless for timing.Threads are a limited resource, they take approximately 200,000 cycles to create and about 100,000 cycles to destroy. By default they reserve 1 megabyte of virtual memory for its stack and use 2,000-8,000 cycles for each context switch. This makes any waiting thread a hugewaste.
Thread.Sleep有它的用途:在 MTA 线程上测试/调试时模拟冗长的操作。在 .NET 中没有其他理由使用它。
Thread.Sleep(n)意味着阻塞当前线程至少可以在n毫秒内发生的时间片(或线程量子)数量。时间片的长度在不同版本/类型的 Windows 和不同的处理器上是不同的,通常在 15 到 30 毫秒之间。这意味着线程几乎可以保证阻塞超过n毫秒。您的线程将在n几毫秒后重新唤醒的可能性几乎是不可能的。 所以,Thread.Sleep对时间来说毫无意义。线程是一种有限的资源,它们大约需要 200,000 个周期来创建和大约 100,000 个周期来销毁。默认情况下,它们为其堆栈保留 1 兆字节的虚拟内存,并为每个上下文切换使用 2,000-8,000 个周期。这使得任何等待线程成为 巨大的浪费。
The preferred solution: WaitHandles
首选解决方案:WaitHandles
The most-made-mistake is using Thread.Sleepwith a while-construct (demo and answer, nice blog-entry)
最容易犯的错误是使用Thread.Sleepwhile 构造(演示和答案,不错的博客条目)
EDIT:
I would like to enhance my answer:
编辑:
我想加强我的答案:
We have 2 different use-cases:
We are waiting because we know a specific timespan when we should continue (use
Thread.Sleep,System.Threading.Timeror alikes)We are waiting because some condition changes some time ... keyword(s) is/are some time! if the condition-check is in our code-domain, we should use WaitHandles - otherwise the external component should provide some kind of hooks ... if it doesn't its design is bad!
My answer mainly covers use-case 2
我们有两个不同的用例:
我们正在等待,因为我们知道应该继续的特定时间跨度(使用
Thread.Sleep,System.Threading.Timer或类似的)我们正在等待,因为某些条件有时会发生变化……关键字是/是某个时间!如果条件检查在我们的代码域中,我们应该使用 WaitHandles - 否则外部组件应该提供某种钩子......如果不是,它的设计很糟糕!
我的回答主要涵盖用例 2
回答by Swab.Jat
SCENARIO 1 - wait for async task completion: I agree that WaitHandle/Auto|ManualResetEvent should be used in scenario where a thread is waiting for task on another thread to complete.
场景 1 - 等待异步任务完成:我同意 WaitHandle/Auto|ManualResetEvent 应该用于一个线程正在等待另一个线程上的任务完成的场景。
SCENARIO 2 - timing while loop: However, as a crudetiming mechanism (while+Thread.Sleep) is perfectly fine for 99% of applications which does NOT require knowing exactlywhen the blocked Thread should "wake up*. The argument that it takes 200k cycles to create the thread is also invalid - the timing loop thread needs be created anyway and 200k cycles is just another big number (tell me how many cycles to open a file/socket/db calls?).
场景 2 - 计时 while 循环:然而,作为一种粗略的计时机制(while+Thread.Sleep)对于 99% 的应用程序来说是完全没问题的,这些应用程序不需要确切知道被阻塞的线程何时应该“唤醒*。它需要的参数创建线程的 200k 个周期也是无效的 - 无论如何都需要创建定时循环线程,而 200k 个周期只是另一个大数字(告诉我打开文件/套接字/db 调用需要多少个周期?)。
So if while+Thread.Sleep works, why complicate things? Only syntax lawyers would, be practical!
因此,如果 while+Thread.Sleep 有效,为什么要使事情复杂化?只有语法律师才会实用!
回答by Swab.Jat
For those of you who hasn't seen one valid argument against use of Thread.Sleep in SCENARIO 2, there really is one - application exit be held up by the while loop (SCENARIO 1/3 is just plain stupid so not worthy of more mentioning)
对于那些没有看到反对在场景 2 中使用 Thread.Sleep 的有效论据的人,确实有一个 - 应用程序退出被 while 循环阻止(场景 1/3 只是愚蠢的,所以不值得更多提到)
Many who pretend to be in-the-know, screaming Thread.Sleep is evil failed to mentioned a single valid reason for those of us who demanded a practical reason not to use it - but here it is, thanks to Pete - Thread.Sleep is Evil(can be easily avoided with a timer/handler)
许多假装知情、尖叫 Thread.Sleep is evil 的人未能为我们这些要求不使用它的实际理由的人提到一个有效的理由 - 但在这里,感谢 Pete - 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;
}
回答by mike
It is the 1).spinning and 2).polling loop of your examples that people caution against, not the Thread.Sleep() part. I think Thread.Sleep() is usually added to easily improve code that is spinning or in a polling loop, so it is just associated with "bad" code.
这是人们警告的示例的 1).spinning 和 2).polling 循环,而不是 Thread.Sleep() 部分。我认为 Thread.Sleep() 通常被添加来轻松改进旋转或轮询循环中的代码,因此它仅与“坏”代码相关联。
In addition people do stuff like:
此外,人们还会做以下事情:
while(inWait)Thread.Sleep(5000);
where the variable inWait is not accessed in a thread-safe manner, which also causes problems.
其中变量 inWait 不是以线程安全的方式访问的,这也会导致问题。
What programmers want to see is the threads controlled by Events and Signaling and Locking constructs, and when you do that you won't have need for Thread.Sleep(), and the concerns about thread-safe variable access are also eliminated. As an example, could you create an event handler associated with the FileSystemWatcher class and use an event to trigger your 2nd example instead of looping?
程序员想要看到的是由 Events 和 Signaling and Locking 构造控制的线程,当你这样做时,你将不需要 Thread.Sleep(),并且也消除了对线程安全变量访问的担忧。例如,您能否创建一个与 FileSystemWatcher 类关联的事件处理程序,并使用一个事件来触发您的第二个示例而不是循环?
As Andreas N. mentioned, read Threading in C#, by Joe Albahari, it is really really good.
正如 Andreas N. 提到的,阅读Joe Albahari 所著的 C# 线程,它真的非常好。
回答by Steve Greene
Sleep is used in cases where independent program(s) that you have no control over may sometimes use a commonly used resource (say, a file), that your program needs to access when it runs, and when the resource is in use by these other programs your program is blocked from using it. In this case, where you access the resource in your code, you put your access of the resource in a try-catch (to catch the exception when you can't access the resource), and you put this in a while loop. If the resource is free, the sleep never gets called. But if the resource is blocked, then you sleep for an appropriate amount of time, and attempt to access the resource again (this why you're looping). However, bear in mind that you must put some kind of limiter on the loop, so it's not a potentially infinite loop. You can set your limiting condition to be N number of attempts (this is what I usually use), or check the system clock, add a fixed amount of time to get a time limit, and quit attempting access if you hit the time limit.
在以下情况下使用睡眠:您无法控制的独立程序有时可能使用常用资源(例如文件),您的程序在运行时需要访问该资源,以及当这些资源正在使用时其他程序您的程序被阻止使用它。在这种情况下,当您在代码中访问资源时,将对资源的访问放在 try-catch 中(以在无法访问资源时捕获异常),并将其放入 while 循环中。如果资源空闲,则永远不会调用 sleep。但是如果资源被阻塞,那么你会休眠一段适当的时间,并尝试再次访问资源(这就是你循环的原因)。但是,请记住,您必须在循环上放置某种限制器,因此它不是潜在的无限循环。
回答by Steve Greene
I agree with many here, but I also think it depends.
我同意这里的很多观点,但我也认为这取决于。
Recently I did this code:
最近我做了这个代码:
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();
}
It was a simple animate-function, and I used Thread.Sleepon it.
这是一个简单的动画功能,我使用Thread.Sleep了它。
My conclusion, if it does the job, use it.
我的结论是,如果它可以完成工作,请使用它。
回答by Jason Nelson
I would like to answer this question from a coding-politics perspective, which may or may not be helpful to anyone. But particularly when you're dealing with tools that are intended for 9-5 corporate programmers, people who write documentation tend to use words like "should not" and "never" to mean "don't do this unless you really know what you're doing and why".
我想从编码的角度来回答这个问题,这可能对任何人都有帮助,也可能没有。但特别是当您处理面向 9-5 名企业程序员的工具时,编写文档的人倾向于使用诸如“不应该”和“从不”之类的词来表示“除非您真的知道自己在做什么,否则不要这样做” “在做什么以及为什么”。
A couple of my other favorites in the C# world are that they tell you to "never call lock(this)" or "never call GC.Collect()". These two are forcefully declared in many blogs and official documentation, and IMO are complete misinformation. On some level this misinformation serves its purpose, in that it keeps the beginners away from doing things they don't understand before fully researching the alternatives, but at the same time, it makes it difficult to find REAL information via search-engines that all seem to point to articles telling you not to do something while offering no answer to the question "why not?"
我在 C# 世界中的其他几个最爱是它们告诉您“永远不要调用 lock(this)”或“永远不要调用 GC.Collect()”。这两个在许多博客和官方文档中被强行声明,IMO 完全是错误信息。在某种程度上,这种错误信息达到了它的目的,因为它使初学者在完全研究替代方案之前避免做他们不理解的事情,但同时,它很难通过搜索引擎找到真正的信息似乎有一些文章告诉你不要做某事,而没有回答“为什么不?”这个问题。
Politically, it boils down to what people consider "good design" or "bad design". Official documentation should not be dictating the design of my application. If there's truly a technical reason that you shouldn't call sleep(), then IMO the documentation should state that it is totally okay to call it under specific scenarios, but maybe offer some alternative solutions that are scenario independent or more appropriate for the other scenarios.
在上,它归结为人们认为“好的设计”或“坏的设计”。官方文档不应该决定我的应用程序的设计。如果确实存在不应该调用 sleep() 的技术原因,那么 IMO 文档应该说明在特定场景下调用它是完全可以的,但可能会提供一些与场景无关或更适合其他解决方案的替代解决方案场景。
Clearly calling "sleep()" is useful in many situations when deadlines are clearly defined in real-world-time terms, however, there are more sophisticated systems for waiting on and signalling threads that should be considered and understood before you start throwing sleep() into your code, and throwing unnecessary sleep() statements in your code is generally considered a beginners' tactic.
明确调用“sleep()”在许多情况下很有用,当最后期限在现实世界中被明确定义时,但是,有更复杂的系统用于等待和发信号通知线程,在您开始睡眠之前应该考虑和理解( ) 到您的代码中,并在您的代码中抛出不必要的 sleep() 语句通常被认为是初学者的策略。
回答by Henrik R Clausen
I have a use case that I don't quite see covered here, and will argue that this is a valid reason to use Thread.Sleep():
我有一个用例在这里我不太清楚,并且会争辩说这是使用 Thread.Sleep() 的正当理由:
In a console application running cleanup jobs, I need to make a large amount of fairly expensive database calls, to a DB shared by thousands of concurrent users. In order to not hammer the DB and exclude others for hours, I'll need a pause between calls, in the order of 100 ms. This is not related to timing, just to yielding access to the DB for other threads.
在运行清理作业的控制台应用程序中,我需要对由数千个并发用户共享的数据库进行大量相当昂贵的数据库调用。为了不影响数据库并在数小时内排除其他人,我需要在通话之间暂停,大约为 100 毫秒。这与时间无关,只是为了让其他线程访问数据库。
Spending 2000-8000 cycles on context switching between calls that may take 500 ms to execute is benign, as does having 1 MB of stack for the thread, which runs as a single instance on a server.
在可能需要 500 毫秒执行的调用之间的上下文切换上花费 2000-8000 个周期是良性的,线程具有 1 MB 的堆栈也是如此,该线程作为服务器上的单个实例运行。

