为了账号安全,请及时绑定邮箱和手机立即绑定

从后台线程更新 UI

从后台线程更新 UI

C#
慕运维8079593 2021-06-02 09:58:29
我有一个后台任务,它位于从外部硬件设备读取数据的循环中。设备公开一个每 100 毫秒递增的“计数器”——当这个值改变时,我从设备中获取下一个数据值并引发一个事件。伪代码:-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什么相同的效果。视图中的事件处理程序没有做太多事情,但这是一个复杂的桌面应用程序,其他后台线程更新其他绑定控件。也许这个特定的过程只是在性能方面使事情超出了边缘,尤其是当我开始拖动窗口时?我想知道是否可以将事件处理程序代码包装在 a 中Task.Run(),(我假设)这会导致它立即将控制权返回给 while 循环,而不必等待事件处理程序执行它的操作。不过,这听起来很hacky - 我是否在为这样的事情找麻烦,特别是考虑到事件处理程序将被调用的频率(每 100 毫秒)?
查看完整描述

2 回答

?
慕妹3146593

TA贡献1820条经验 获得超9个赞

您遗漏了一些细节,主要是事件处理程序在 GUI 线程上执行的操作。Invoke 或 BeginInvoke 等。

但如果保护您的数据最重要,还有另一种选择:将新数据推送到 ConcurrentQueue。引发 Received 事件是可以的,但可选,您可能不需要它。

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

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


查看完整回答
反对 回复 2021-06-05
?
森林海

TA贡献2011条经验 获得超2个赞

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


这是一种方法(该方法必须是 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

        });

}


查看完整回答
反对 回复 2021-06-05
  • 2 回答
  • 0 关注
  • 114 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信