主 UI 窗口冻结,尽管计算在不同的任务中运行

本文关键字:任务 运行 计算 窗口 UI 冻结 | 更新日期: 2023-09-27 18:37:15

我有一个关于解决UI冻结的问题。

介绍:我目前正在为给定的分析器工具编程基于 OPC 的在线报警阅读器。此工具从 Excel 工作表接收数据,使用规则库和拓扑模型分析此数据,并使用 TreeViewItems 显示结果。我的任务是用实时警报阅读器替换 excel 工作表阅读器。完成后,我可以将我的软件连接到服务器,并在每次创建新警报时接收数据包。

问题:我将新数据传输到主类并从那里传输到分析器类的解决方案是将数据保存在列表中,将它们添加到 EventArgs 并引发事件。主类中的处理方法接收此数据,为分析器启动一个新任务 (Task>),并将其结果返回到主线程。这种构造应该将计算过程与UI分离 分析过程大约需要1.3秒,使用示例数据。新数据平均每 2 秒到达一次。2秒是同一时间的最高刷新时间。

附上处理方法的代码片段

    Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
                    ()=>
                        {
                            /*if the alarmlist is emty, set the new alarms as alarmlist, 
                             * else add the new alarms to the present list*/
                            if (AlarmList1.Count == 0)
                                AlarmList1 = e.CurrentAlarms;
                            else listModifier.mergeList(e.CurrentAlarms, AlarmList1);
                            /*Start the analysis process in a seperate task and return the Alarm_Group-List*/
                            return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1); 
                        });
                Cursor = Cursors.Wait;
                List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;
                showAlarmLog(alarmGroupList);
                Cursor = Cursors.Arrow;

不幸的是,当我开始分析过程时,UI 仍然卡住了,我什至不知道我每两秒启动一个新线程(新警报的平均出现时间)的概念是否是一个合理的概念。我想问题出在showAlarmLog内部,但它有很多代码。如果需要,我也会发布此代码。

我将感谢有关此问题的任何建议,甚至是"您的概念是废话,请尝试这个想法:......"很高兴知道。

亲切问候拉里莫

主 UI 窗口冻结,尽管计算在不同的任务中运行

问题是这个调用:

List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;

阻止 UI 线程,直到后台任务完成。

处理此问题的方法是使用继续任务,而不是等待结果:

Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
    ()=> {
        // Keep existing code
        return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1); 
         });
// Make a continuation here...
analysertask.ContinueWith( t =>
    {
        Cursor = Cursors.Wait;
        List<Analyser.ALARM_GROUP> alarmGroupList = t.Result;
        showAlarmLog(alarmGroupList);
        Cursor = Cursors.Arrow;
    }, TaskScheduler.FromCurrentSynchronizationContext());

通过将其计划为延续,它将在第一个任务完成时运行。通过使用 TaskScheduler.FromCurrentSynchronizationContext ,您说在执行时将其封送回 UI 线程。


请注意,使用 .NET 4.5/C# 5 可以更轻松地完成此操作。 使用 C# 5,可以将其编写为:

var analysertask = Task.Run(
    ()=> {
        // Keep existing code
        return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1); 
         });
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = await analysertask;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;

但是,这要求方法本身使用 new async 关键字进行标记。