2

我有一个关于解决 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 内部,但代码很多。如果需要,我也会发布此代码。

我会很感激关于这个问题的任何建议,即使是“你的概念是废话,试试这个想法:......”会很高兴知道。

亲切的问候拉里莫

4

1 回答 1

4

问题是这个调用:

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;

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

于 2012-06-15T16:56:56.730 回答