Winforms-Timer:在异步线程中启动,省略了ui线程中的tick事件

时间:2013-11-29 08:44:51

标签: c# multithreading winforms asynchronous timer

我必须再为我的主ui线程实现两个异步线程。

One(T_A1)获取一些db数据并返回,ui可能仍处于活动状态并接收用户交互(可能将来更改回一个线程)和另一个线程(T_A2),该线程轮询某些硬件以获取特殊值。 我的winforms-ui上还有一个计时器,一个简单的windows.forms.Timer对象。

当T_A2(在自己的对象中启动)向ui线程抛出一个事件来处理它时,它仍然在异步线程中,但是在mainform中处理。 在这个事件处理程序中,我启动计时器。 (只需通过timer.Start();)

我记录了tick事件,但我没有得到它,它根本就不会发生,所以我没有日志条目。

但是当我在Invoke / BeginInvoke的帮助下启动计时器时,我编组了要在ui中执行的计时器的启动。 然后发生tick事件,我得到我的日志条目。


我的问题是:为什么?

如果我不使用Invoke / BeginInvoke,我会在线程中启动计时器,这会抛出事件(T_A2),所以预计在这个线程中也会出现勾号,但线程已经死了?

或者可能是什么原因?

2 个答案:

答案 0 :(得分:2)

为什么?很简单,线程是危险的。它具有做你希望它会做什么的诀窍。

Winforms中的许多GUI类都不是线程安全的,必须使用Control.BeginInvoke来确保修改其属性或在创建对象的完全相同的线程上调用它们的方法。这是一个非常基本的限制,创建线程安全的代码困难

即使是.NET中的简单类也不是线程安全的,没有一个集合类。你有一些自由度,比如List<>,你可以通过仔细使用 lock 关键字来确保自己的线程安全,以确保只从一个线程访问List对象。仍然很难正确地做。

当类不简单并且与其他类和操作系统进行非平凡的交​​互时,这将停止实用。 .NET中很少有类作为Control,它有数百种方法和属性。因此,微软本身刚刚宣布使其成为线程安全的工作变得不可能,就像他们使用List<>所做的那样,并且没有希望你可以自己添加足够的锁来使其成为线程安全的。死锁几乎得到保证。微软也没有给你足够的访问内部注入所需的锁定,他们认为这不值得。

所以你必须只使用同一个线程中的对象来保证它的安全,它们确实为你提供了所需的工具。其中包括BeginInvoke和各种额外的帮助,如BackgroundWorker,TaskScheduler.FromCurrentSynchronizationContext和async / await关键字。当你做错时你会得到InvalidOperationException,这是一个非常有用的例外。

您遇到的Timer类的具体实现细节是它是一个非常简单的类,实际上线程安全的。在工作线程上启动或停止它可以正常工作并且是一件安全的事情。请注意,您没有得到违反线程要求时通常会得到的InvalidOperationException。不幸的是,它取决于你没有处理的实现细节,而且永远不会。它的Tick事件由一个调度程序循环引发,这种循环来自Application.Run()。并且您没有在工作线程上调用Application.Run()。所以计时器永远不会滴答。

这里适当的解决方案是在你的工人上调用Application.Run(),这是一个给你两个新问题的解决方案。你已经有了一个非常好的线程,它调用了Application.Run()。使用Control.BeginInvoke()来使用它。

答案 1 :(得分:1)

答案很简单:

System.Windows.Forms.Timer与应用程序的消息循环紧密耦合。它完全取决于消息循环,因此,它只适用于主UI线程。它不会创建自己的线程。你的T_A2线程上没有消息循环,所以它就会死掉 - 即使你的线程继续,它也不会导致任何定时器事件。

如果你想要一个与消息循环无关的计时器,你必须使用System.Timers.Timer,它以完全不同的方式工作,并且也适用于你的场景。

相关问题