从后台线程

时间:2018-05-09 11:44:00

标签: c# wpf multithreading task-parallel-library

我有一个后台任务,它位于从外部硬件设备读取数据的循环中。该设备公开了一个"计数器"每100ms递增一次 - 当此值更改时,我从设备获取下一个数据值并引发事件。伪代码: -

public event HasDataEventArgs DataReceived;

while (some condition)
{
    // Wait for device counter to increment
    while (DeviceCounterValue == _lastDeviceCounterValue)
    {
       Thread.Sleep(3);
    }
    _lastDeviceCounterValue = DeviceCounterValue;

    // Read data value from device
    var data = GetDataFromDevice();

    // Raise my event
    DataReceived(this, new HasDataEventArgs(data)); 
}

我还有一个订阅此事件的UI视图。事件处理程序在图表上绘制数据值并设置一些绑定属性。

大部分时间这一切都运行良好,但如果我(比如说)拖动窗口或打开模态对话框,它偶尔会导致数据丢失。似乎发生的事情是外部设备计数器继续增加,但是' while'循环有效地暂时停滞,因此错过了这些变化。 非常偶尔我会看到相同的效果,即使我没有弄乱UI中的任何内容。

视图中的事件处理程序并没有做太多,但这是一个复杂的桌面应用程序,其他后台线程更新其他绑定控件。也许这个特殊的过程只是在性能方面倾斜,特别是一旦我开始拖动窗户?

我想知道是否可以将事件处理程序代码包装在Task.Run()中,(我假设)会导致它立即将控制权返回到while循环,而不是必须等待事件处理程序执行它的东西。听起来很糟糕 - 我是不是要问这样的问题,特别是考虑到调用事件处理程序的频率(每100毫秒)?

2 个答案:

答案 0 :(得分:2)

你遗漏了一些细节,主要是eventhandler在GUI线程上做的事情。调用或BeginInvoke等。

但是还有另一种选择,如果保护您的数据最重要:将新数据推送到ConcurrentQueue。然后提出收到的事件是可以的但是可选的,你可能不会需要它。

主线程可以在自己的时间内清空队列。例如使用Timer。

您的屏幕更新仍会断断续续,但您不应再丢失数据。

答案 1 :(得分:1)

你必须拆分(线程)两件事:背景工作和绘图工作。一般来说,这是如何做这些事情的方式,但具体来说 - 如果您的绘图需要时间,那么您的工作线程有可能无法按时处理传入的数据,并且您可能会丢失一些数据/省略(这是你实际观察到的)。

以下是一种方法(该方法必须是UI类的成员 - 窗口,用户控件等):

void OnDataReceived(object sender, DataEventArgs e)
{
    // here we're in the context of the working thread

    // this call will return immediately giving control back to the working thread
    Dispatcher.BeginInvoke(
        DispatcherPriority.Normal,
        (Action)delegate
        {
            // here we are in the context of the UI thread
        });
}