为什么使用System.Threading.Thread.Sleep()是一种不良实践?

4

我正在编写一款与一个庞杂的第三方系统通过复杂的API进行交互的应用程序。

有时候系统内部会出现一些错误,但如果等到我的程序面对这些错误时,可能已经太晚了。

因此,我使用一个单独的线程来检查系统状态,如下所示:

while (true)
{
    ask_state();
    check_state();
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
}

无论我是每100毫秒还是每分钟检查系统状态,都没有太大关系。

但我听说使用Thread.Sleep()是一种不好的做法。为什么?在这种情况下我应该怎么做呢?


如果您想定期检查某些内容,为什么不使用计时器呢? - germi
2
主要问题是“计时器是否更好?为什么Thread.Sleep()不好?” - user2862319
1
因为你在不必要地保持一个线程的活动。当然,这是否真的重要取决于你的上下文。如果你有一个简单的应用程序,那么使用定期计时器或专用线程很可能不会产生任何性能差异,因为计时器只会强制从线程池中选择一个线程保持活动状态。 - Polity
1
一般来说(特别是在使用大量多线程应用程序时),有更具描述性的替代方案,例如计时器。 - Polity
1
我听说使用Thread.Sleep()是一个不好的做法 - 几乎任何东西都可以被误用。你上面的代码工作了吗?如果是这样,请保留它。 - Martin James
@MartinJames 当然,它确实如此 =) - user2862319
3个回答

5
一个原因是 Thread.Sleep() 阻塞了你的代码,使其无法执行其他任务。现代技术的努力是尽可能减少阻塞。例如,node.js 是一种非阻塞语言。
更新:我不知道 C# 中 Timer 类的基础设施。也许它也会阻塞。
你可以安排一个任务每 100 毫秒检查第三方 API。这样,在这 100 毫秒内,你的程序可以执行其他任务。
更新:这个类比可能有所帮助。如果我们把操作系统比作医院,把线程比作医院里的护士,那么主管(程序员)可以选择一种策略:
1. 要求每个护士(线程)只照看一个病人(一个工作、一个任务),即使在每次检查之间等待一个小时(Sleep() 方法)。 2. 要求每个护士检查每个病人,并在下次检查之前的间隔期间继续检查其他病人。
第一种模型是阻塞的,不可扩展。但在第二种模型中,即使只有少数护士,你也可能能为许多病人提供服务。

单独的线程并不意味着它是非阻塞的。在此期间,其他线程将被暂停,并且不会返回到线程池以执行任务。 - Saeed Neamati
另一个线程将被暂停,并且不会返回到线程池以执行该作业。您能详细解释一下它是如何实现的吗?check thread、ask_state和check_state()都没有任何锁定。 - user2862319
@Polity,线程就像工人一样。即使它不是来自线程池的线程,它也是正在使用的操作系统资源。而这个资源应该等待100毫秒再次检查。 - Saeed Neamati
1
不是的 @user2862319,我并不是要说那个 :D。我只是想说他们反对使用 Sleep 的一个原因是它会阻塞程序。至于 Timer 的基础设施,我不太清楚,也许在内部也使用了 Sleep - Saeed Neamati
3
另一方面,使用定时器意味着编写代码时需要将其作为可能混乱的异步状态机编写,而不是像使用单独线程一样内联编写,其中Sleep()可以在任何函数深度调用。从定时器管理线程触发定时器至少需要与Sleep()调用一样多的上下文切换,如果定时器回调信号到另一个正在等待输入消息的线程,则可能需要两倍上下文切换。 - Martin James
显示剩余8条评论

3
因为唯一关闭正在等待Sleep的线程的方法是要么a)等待Sleep结束,要么b)使用Thread.AbortThread.Interrupt

1 如果是一个长时间的休眠,则(a)不太适合,如果您想保持响应灵敏度。如果代码恰好在此时内部Sleep中,则(b)相当麻烦。

如果您想以适当的方式中断睡眠行为,最好使用可等待对象(例如ManualResetEvent),这样甚至可以将等待对象的等待放置在while条件中,以明确导致线程退出的原因。


1我在这个实例中使用了shutdown,因为这是需要跨线程通信的非常普遍的场景。但对于任何其他跨线程信号或通信,同样的论点也适用,如果不是shutdown,则Thread.AbortThread.Interrupt更不合适。


虽然你在第一段所说的是正确的,但通常并不重要,例如如果线程预计在应用程序的整个生命周期内运行。 - Martin James

0
我会设置一个定时器,等待我的检查方法完成,你想使用无限循环还是你展示的代码不完整?好的,这是我所说的示例:
public void myFunction()
{
int startCount = Environment.TickCount;
ask_state();
check_state();

while (true)
{
if (Environment.TickCount - startCount >= 20000) //two seconds 
{
break;
}
Application.DoEvents();
}
}

//Now you have an organized function that makes the task you want just call it every
// time interval, again you can use a timer to do that for you

private void timer_Tick(object sender, EventArgs e)
{
            myFunction();
}

祝好运


如果一切正常,我的程序应该运行。并且如果它正在运行,它应该检查系统状态。将在未来版本中更改为while(running)。 - user2862319
好的,所以使用计时器而不是sleep(),这是我所说的一个示例: - chouaib

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