如何制作一个C#计时器来触发主线程中的事件?

长话短说,我需要.Net中的精确计时器 – 精确到毫秒 – 这意味着,如果我告诉它在10ms通过时触发事件,它必须这样做,+ -1ms。 内置的.Net Timer类似乎具有+ -16ms的精度,这对我的应用来说是不可接受的。

我发现这篇文章http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer ,它提供了一个完全符合我需要的计时器代码(甚至更多 – 具有精确度,以微秒为单位) 。

但问题是,OnTimer等效似乎是在另一个线程中执行的。 所以,如果我添加一些代码,请说:

label1.Text = "Hello World"; 

我会得到一个例外,因此我必须像这样写它:

 Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";})); 

根据我的理解,这是因为OnTimer事件是从计时器的线程触发的 – 时间过去直到有足够的时间超过Interval,然后触发下一个OnTimer事件。 .Net Timer没有这样的问题 – 在.Net Timer的OnTimer中,我可以自由地修改控件的成员。

问题:我应该更改什么,以便我的计时器将在主线程中运行它的OnTimer事件? 添加“Invoke”是唯一的选择吗?

虽然有几种方法可以实现,但我通常更喜欢的是让计时器在创建时捕获SynchronizationContext.Current的值。 当在UI线程中时,该值将包含当前同步​​上下文,该上下文可用于在UI上下文中执行消息循环中的方法。 这适用于winforms,WPF,silverlight等。所有这些范例都设置了同步上下文。

只要在创建计时器时获取该值,假设它是在UI线程中创建的。 如果你想要一个可选的构造函数/属性来设置值,那么你就可以使用它,即使你没有在UI线程中创建计时器,尽管大多数时候都不需要。

然后只需使用该上下文的Send方法来触发事件:

 public class Timer { private SynchronizationContext syncContext; public Timer() { syncContext = SynchronizationContext.Current; } public event EventHandler Tick; private void OnTick() { syncContext.Send(state => { if (Tick != null) Tick(this, EventArgs.Empty); }, null); } //TODO other stuff to actually fire the tick event } 

没有办法将UI元素访问分配给主线程。 如果更新UI元素实际上是您打算在计时器回调中唯一要做的事情,那么就忘记了您的计时器精度要求。 用户将看不到16ms和50ms之间的差异。

否则,在计时器回调中执行时间关键工作,并将其余的UI工作分派给主线程:

 void OnTimer() { // time critical stuff here Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";})); } 

在wpf中,您可以使用调度程序类在UI线程中调度消息:

 Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=> label1.Text = "Hello World")); 

在winforms中,您需要调用invoke方法:

 this.Invoke(()=> label1.Text = "Hello World");