为什么线程。睡眠这么有害

我经常看到它提到 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);
}
}
234089 次浏览

打电话给 Thread.Sleep的问题是 这里解释得很简洁:

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

Thread.Sleep(n)表示阻塞当前线程的至少一个数字 的时间片(或线程量子) ,可以发生在 n 毫秒。 不同版本/类型的时间片的长度是不同的 Windows 和不同的处理器,通常在15到30之间 这意味着线程几乎肯定会阻塞 超过 n毫秒。你的线程 完全在 n毫秒后重新唤醒是不可能的 不可能是不可能的。

线程是一种有限的资源,它们需要大约200,000个周期 创建并销毁约100,000个周期。默认情况下,它们 为其堆栈保留1兆字节的虚拟内存,并使用2,000-8,000 这使得任何等待的线程 a 巨大的浪费

首选解决方案: WaitHandles

最常犯的错误是使用带 while 构造的 Thread.Sleep(演示和回答博客写得不错)

编辑:
我想强调一下我的回答:

我们有两种不同的用例:

  1. 我们在等待,因为我们知道 具体的时间跨度,我们应该继续(使用 Thread.SleepSystem.Threading.Timer或类似)

  2. 我们在等待,因为有些情况会改变。 关键字是/是 有时候! 如果条件检查是在我们的代码域中,我们 应该使用 WaitHandles-否则外部组件应该 提供一些钩子... 如果它没有它的设计是坏的!

我的答案主要包括用例2

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

场景2-timewhile 循环: 但是,作为 粗糙计时机制(while + Thread。睡眠)对于99% 的应用程序是完全没问题的,这些应用程序不需要知道当被阻塞的线程应该“唤醒 * 时 没错。创建线程需要200k 周期的参数也是无效的——无论如何都需要创建计时循环线程,200k 周期只是另一个大数字(告诉我打开一个文件/socket/db 调用需要多少个周期?).

因此,如果当 + 线程。睡眠工程,为什么复杂的东西? 只有语法律师会,是 很实际

对于那些还没有看到反对使用 Thread 的有效论据的人来说。在场景2中休眠,确实有一个应用程序退出被 while 循环阻止 (场景1/3简直愚蠢至极,不值一提)

许多人假装知情,尖叫着。睡眠是邪恶的,对于我们这些需要一个实际的理由不使用它的人来说,没有提到一个单一的有效的理由——但是在这里,感谢 Pete-睡眠是邪恶的(使用计时器/处理器可以很容易地避免)

    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;
}

它是1)。旋转和2)。轮询示例的循环,人们警告不要使用 ,而不是 Thread。 Sleep ()部分。 我觉得是 Thread。Sleep ()通常用于轻松改进正在旋转或处于轮询循环中的代码,因此它只与“坏”代码相关联。

此外,人们还会做这样的事情:

while(inWait)Thread.Sleep(5000);

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

程序员希望看到的是由 Events、 Signaling 和 Lock 构造控制的线程,当您这样做时,就不需要 Thread 了。Sleep ()以及对线程安全变量访问的关注也被消除了。例如,您可以创建一个与 FileSystemWatcher 类相关联的事件处理程序,并使用一个事件来触发第二个示例而不是循环吗?

正如安德烈亚斯 N 提到的,阅读 C # 中的线程,作者 Joe Albahari,它真的非常非常好。

当独立程序(你无法控制的)有时可能使用一个常用的资源(比如文件) ,你的程序在运行时需要访问这个资源,当这个资源被其他程序使用时,你的程序被阻止使用它。在这种情况下,在访问代码中的资源时,将对资源的访问放在 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

我的结论是,如果它起作用了,就用它。

我想从编码政治的角度来回答这个问题,这可能对任何人都有帮助,也可能没有帮助。但是,特别是当你处理的工具是为9-5个公司程序员准备的时候,编写文档的人往往使用“不应该”和“从不”这样的词来表示“除非你真正知道你在做什么和为什么,否则不要这样做”。

在 C # 世界中,我最喜欢的还有一些建议: “永远不要调用 lock (this)”或“永远不要调用 GC.Collect ()”。在许多博客和官方文档中,这两个词被有力地声明了,而 IMO 则完全是错误的信息。在某种程度上,这种错误信息有它的目的,因为它使初学者远离做他们不明白的事情之前,充分研究的替代方案,但同时,它使得很难找到真正的信息,通过搜索引擎,所有似乎都指向文章告诉你不要做什么,但没有提供答案的问题“为什么不?”

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

很明显,在很多情况下调用“ sleep ()”是有用的,因为截止日期是在现实世界中明确定义的。然而,在你开始向代码中添加 sleep ()之前,应该考虑和理解更复杂的等待和发送线程信号的系统,在代码中添加不必要的 sleep ()语句通常被认为是初学者的策略。

我有一个用例,我在这里没有完全看到,并将争辩说,这是一个有效的理由使用线程。睡眠() :

在一个控制台应用运行的清理作业中,我需要对数千个并发用户共享的数据库进行大量相当昂贵的数据库调用。为了不打击数据库和排除其他小时,我需要一个暂停之间的调用,在100毫秒的顺序,这是没有关系的时间,只是让访问数据库的其他线程。

在上下文切换上花费2000-8000个周期,可能需要500毫秒才能执行的调用是良性的,对于作为服务器上的单个实例运行的线程来说,有1MB 的堆栈也是良性的。

我认为使用计时器是一个不错的选择。如果使用 Thread.sleep (x) ,每 x 毫秒执行一次某个操作,那么该操作将在每 x + y 秒执行一次,其中 y 是执行操作所需的时间。