Timer.Elapsed和BeginInvoke仍然落后于GUI

时间:2016-12-23 16:39:02

标签: c# wpf timer dispatcher

有一段第三方API代码SomeWork()需要每隔3秒运行一次以监控某些物理设备。我想使用System.Timers.Timer并且它的Elapse事件是异步的,但是它每隔3秒就滞后于我的GUI操作。我尝试使用Dispatcher.BeginInvoke进行SomeWork()但我仍然在GUI上遇到相同的延迟,即使SomeWork()实际上没有更新任何GUI,也不应该占用GUI线程。

private void StartTimerAtStartup()
{
    System.Timers.Timer connTimer = new System.Timers.Timer();
    connTimer.Elapsed += new ElapsedEventHandler(MyTimer_Elapsed);
    connTimer.Interval = 3000; 
    connTimer.Enabled = true;
}

private void MyTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (!Dispatcher.CheckAccess())
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Send, (ElapsedEventHandler)MyTimer_Elapsed, sender, e);
        return;
    }

    // Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(
    //             () => SomeWork())
    // );

    SomeWork();
}

[EDIT1]

如果我删除所有Dispatcher并直接调用SomeWork(),那么我得到一个异常,我无法访问此线程。

[EDIT2]

SomeWork()是第三方API,用于侦听物理设备和接收数据包,但不接触GUI。我尝试了各种方法来执行Dispatcher.BeginInvoke,我遇到以下情况,我无法理解。为什么使用lamda会滞后GUI但是做一个委托并不会落后于GUI?

// This lags the GUI
Dispatcher.BeginInvoke(new Action( () => SomeWork() ));

// This does not lag the GUI. Application runs smooth, 
// but I do not understand why
Dispatcher.BeginInvoke((Action)delegate () { SomeWork(); });

1 个答案:

答案 0 :(得分:2)

您调用GUI线程的Dispatcher。因此,BeginInvoke调用异步工作到UI线程,并使您的UI滞后。 您的计时器在不同于UI线程的情况下触发Elapsed事件。所以你只需在事件处理程序中调用你的方法。

但是,如果您需要每3秒钟严格调用一次API,而不是考虑执行SomeWork所需的时间或在分离的线程中完成工作。然后你可以使用任务来实现这一目标。

 Task.Factory.StartNew(SomeWork);

但在这种情况下,你将不得不处理可能的竞争条件并同步你的工作。