Thread.Sleep 还是 Thread.Timer 挂机系统?

Posted

技术标签:

【中文标题】Thread.Sleep 还是 Thread.Timer 挂机系统?【英文标题】:Thread.Sleep or Thread.Timer hanging system? 【发布时间】:2014-08-04 20:10:37 【问题描述】:

我已经四处寻找一个多星期了,但我找不到其他人遇到与我在这里看到的类似的问题。

我正在使用在 Windows XP 上运行并在 Visual Studio 2003 中开发的旧应用程序。大约 3 周前突然间,该应用程序变得无响应,操作员必须向 Windows 三指敬礼 (CTRL -ALT-DEL) 调出任务管理器,终止应用程序进程(显示为 Not Responding)并重新启动应用程序。

我设法让它在调试器中发生一次,当我暂停应用程序时,它正在等待系统从尝试设置 System.Windows.Forms.Timer.Interval 属性返回。

这是对象,它似乎挂在哪里,这不是它在源代码中的编写方式

internal static System.Windows.Forms.Timer timerMtimeOut;

// This is in an initialization method.
timerMtimeOut = new System.Windows.Forms.Timer();   
timerMtimeOut.Tick += new System.EventHandler(timerMtimeOut_Tick);

// this is how it's value is set.
timerMtimeOut.Interval = 1 * msec;  // <-- This is where it was in the debugger
timerMtimeOut.Enabled = true;


private static void timerMtimeOut_Tick(object sender, System.EventArgs e)
           
    mTimeOut +=timerMtimeOut.Interval/msec;

应用程序基本上变得无响应,必须使用任务管理器关闭并重新启动。

多年来一直运行良好,然后大约 2-3 周前才开始出现这种情况。

还有其他人看到过这种行为吗?

【问题讨论】:

此类问题大多与编码有关,为什么不显示您怀疑挂起的代码。 创建了多少线程...调用/创建了多少次计时器? 我已经为特定的 Timer 对象添加了代码。这是一个单线程,但定时器被多次启用和禁用。自从最初编写(在我之前)以来,这段代码几乎没有受到任何影响。我所做的是在各自的 timerMtimeOut.Enable = true 和 false 之后删除 timerMtimeOut.Start() 和 Stop() 调用。 值得一提的是,我们有两台机器正在运行此源代码,并且应用程序在第二台机器上没有变得无响应。 我想你已经知道你可能正在追逐一只野鹅。仅仅因为你看到你的机器上发生了一些事情,而这就是代码损坏的地方,并不一定意味着另一台机器上正在发生的事情。无论如何,是否可以完全禁用计时器并查看问题是否在另一台机器上消失,并确定它与此有关?您的评论表明您可能已经这样做了,但不清楚。 【参考方案1】:

最可能的原因是您使用计时器的方式并非专为该计时器而设计。第一行代码给出了提示:

internal static System.Windows.Forms.Timer timerMtimeOut;

没有充分的理由声明System.Windows.Forms.Timer 静态,因为这个特定的计时器被设计为链接到特定的窗口(窗体实例),并且会导致在该窗体的主线程中触发滴答事件。它通过在计时器达到滴答声时将消息发送到线程消息队列来实现。然后在表单线程 (WndProc) 中处理该消息以及所有其他消息,例如鼠标移动和键盘输入。

如果您实例化多个访问计时器的窗口,那么问题迟早会出现。

当操作系统说某个进程没有响应时,它会通过查看主线程消息队列中的消息如何被处理来检测这一点。如果它们没有被处理,那么要么主线程正在运行一个冗长的例程,要么已经进入了某个无限循环。从主线程调用Thread.Sleep()(如您的问题标题中所述)将导致消息处理暂停(并将阻止在睡眠期间处理滴答声)。

冗长的程序应在单独的线程中运行,以保持应用程序响应。您可能希望显示一个进度条并让用户有机会中止该过程。如果您在主线程中运行冗长的进程,这很难做到。通过在循环中的某处调用Application.DoEvents() 有一个肮脏的解决方法,它将即时处理消息队列。但是在使用此解决方法时,您应该非常小心地禁用 UI 按钮,否则您的用户可能会多次(递归)启动该进程或更改您的进程所依赖的其他状态。

有点跑题了,但变量的名称表明您正在尝试计算漫长过程中的间隔数?请注意,如果该冗长进程在 Form 的主线程中运行,则计时器的滴答事件将在消息循环中排队,并且仅在该冗长进程完成其工作并恢复消息处理后才会执行。您在 cmets 中提到的使用 DateTime 字段的方法比使用计时器更简单、更安全、更好。

如果您需要一个在滴答声被击中时立即执行代码的计时器,您可以使用System.Threading.Timer 而不是System.Windows.Forms.Timer。如果您需要从多个线程控制它,请改用System.Timers.Timer(那个是线程安全的)。请注意,这些计时器将在不同的线程中执行它们的事件。另请注意,您不应从表单主线程以外的其他线程操作控件或表单。一个简单的方法是使用 Control.Invoke() 方法在框架中内置,该方法又将使用消息循环对操作进行排队。

【讨论】:

以上是关于Thread.Sleep 还是 Thread.Timer 挂机系统?的主要内容,如果未能解决你的问题,请参考以下文章

timeunit的sleep和thread的sleep有啥区别

C#中关于Thread.sleep(1000)的问题!!

Java:关于Thread.sleep()

如果 Task.Delay 优于 Thread.Sleep,为啥本书中的示例使用 Thread.Sleep?

多线程编程里的thread.sleep问题

为啥 Thread.Sleep 如此有害