我有一个后台任务,它位于从外部硬件设备读取数据的循环中。该设备公开了一个"计数器"每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毫秒)?
答案 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
});
}