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

时间:2014-08-04 20:10:37

标签: c# multithreading visual-studio timer visual-studio-2003

我一直在寻找一个星期以上,而且我还没有找到其他人遇到类似问题的人,而我在这里看到了。

我正在使用在Windows XP上运行并在Visual Studio 2003中开发的OLD应用程序。大约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周前才开始发生。

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

1 个答案:

答案 0 :(得分:1)

最可能的原因是您正在以不是为其构建的方式使用计时器。第一行代码给出了一个提示:

internal static System.Windows.Forms.Timer timerMtimeOut;

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

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

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

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

有点偏离主题,但变量的名称暗示你试图在漫长的过程中计算间隔的数量?请注意,如果在Form的主线程中运行该冗长的进程,则计时器的tick事件将在消息循环中排队,并且仅在冗长的进程完成其工作并且恢复消息处理之后才执行。使用DateTime字段在评论中提到的方法比使用计时器更简单,更安全,更好。

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