我正在运行一个基本的单线程应用程序.
通常,在调用时System.Windows.Forms.MessageBox.Show()
,可以预期此调用会有效阻止进一步执行,直到此方法返回为止.
但是,在使用的时候System.Windows.Forms.Timer
,似乎*同*线程以某种方式释放本身和Timer
的Tick
事件被烧成这个相同的线程.
到底是怎么回事?我觉得这可能与穿线公寓有关,但我想澄清一下.
作为控制台应用程序以其最简单的形式重新创建如下:
class Program { static void Main(string[] args) { new Program(); while (true) { System.Windows.Forms.Application.DoEvents(); } } private System.Windows.Forms.Timer timer; public Program() { timer = new System.Windows.Forms.Timer() { Interval = 2000 }; timer.Tick += timer_Tick; timer.Start(); } private void timer_Tick(object sender, EventArgs e) { Console.WriteLine(string.Format("Thread {0} has entered", Thread.CurrentThread.ManagedThreadId)); var result = MessageBox.Show("Test"); Console.WriteLine(string.Format("Thread {0} has left", Thread.CurrentThread.ManagedThreadId)); } }
输出:
线程10已进入
线程10已进入
线程10已进入
线程10已进入
线程10已进入
Matthew Wats.. 6
当显示模式窗口(如消息框)时,Windows消息泵将继续运行.
如果没有,当您在其前面移动模态窗口时,模态窗口后面的窗口的显示将不会更新.
由于Windows消息仍在被抽取,"WM_TIMER"消息仍将被发送到非前景窗口,因此您将看到您注意到的行为.
需要注意的关键是Windows计时器会导致Windows将"WM_TIMER"消息发布到窗口的事件队列中,并且只要窗口的消息泵正在运行,就会继续处理计时器事件.
避免这种重新进入问题的一种常见方法是在处理滴答时禁用计时器.
例如,将您的tick处理代码放入一个调用的方法中handleTimer()
,然后像这样处理tick:
private void timer_Tick(object sender, EventArgs e) { timer.Enabled = false; try { handleTimer(); } finally { timer.Enabled = true; } }
(如果发生异常,您可能不希望重新启用计时器,在这种情况下,您将不需要try/finally
上面的逻辑.)
当显示模式窗口(如消息框)时,Windows消息泵将继续运行.
如果没有,当您在其前面移动模态窗口时,模态窗口后面的窗口的显示将不会更新.
由于Windows消息仍在被抽取,"WM_TIMER"消息仍将被发送到非前景窗口,因此您将看到您注意到的行为.
需要注意的关键是Windows计时器会导致Windows将"WM_TIMER"消息发布到窗口的事件队列中,并且只要窗口的消息泵正在运行,就会继续处理计时器事件.
避免这种重新进入问题的一种常见方法是在处理滴答时禁用计时器.
例如,将您的tick处理代码放入一个调用的方法中handleTimer()
,然后像这样处理tick:
private void timer_Tick(object sender, EventArgs e) { timer.Enabled = false; try { handleTimer(); } finally { timer.Enabled = true; } }
(如果发生异常,您可能不希望重新启用计时器,在这种情况下,您将不需要try/finally
上面的逻辑.)