c# - 尽管计算在不同的任务中运行,但主 UI 窗口卡住

标签 c# multithreading task-parallel-library

我有一个关于解决 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;

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

我将感谢有关此问题的任何建议,即使是“你的概念很糟糕,尝试这个想法:......”也会很高兴知道。

亲切的问候 拉里莫夫

最佳答案

问题是这个调用:

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

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

处理这个问题的方法是 use a continuation task而不是等待结果:

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;

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

关于c# - 尽管计算在不同的任务中运行,但主 UI 窗口卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11055042/

相关文章:

c# - 使用扩展方法实例化类的实例

c++ - 如果它的对象在多个线程中运行,我们是否需要锁定类成员函数

c# - 如何检查所有任务是否已正确完成?

c# - 异步等待代码执行不符合预期

.net - Task.Delay 在 .NET 4 中没有记录吗?

c# - 空捕获是否与 try-catch 语句中的 "catch Exception"相同?

c# - 如何在 Xamarin.Forms 中使用 XAML 集 CachingStrategy 子类化 ListView

c# - 在 C# winform 中绘图相当慢

C#线程在运行时重新初始化

java - SingleThreadExecutor VS 普通线程