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

Ist*_*tel 11 c# timer

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

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

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

label1.Text = "Hello World";
Run Code Online (Sandbox Code Playgroud)

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

Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
Run Code Online (Sandbox Code Playgroud)

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

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

Ser*_*rvy 11

虽然有几种方法可以实现,但我通常更喜欢的是让计时器捕获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
}
Run Code Online (Sandbox Code Playgroud)

  • @dtb 很明显,在消息泵到达之前,该方法实际上不会被执行。如果队列是空的,这应该和任何其他编组到 UI 线程的东西一样快,这当然可能不够快。不过,我看不出任何其他方法会更快。 (2认同)