从 Task.ContinueWith 更新 UI 标签

4

我正在开发一个Winform应用程序。该MethodBackgroundWorker线程启动。对不起,我之前没有提到这一点。

private void Method()
{
 tasks[i] = Task.Factory
           .StartNew(() => fileProcessor.ProcessEachMachine(mdetail))
           .ContinueWith(UpdateLabel, TaskContinuationOptions.OnlyOnRanToCompletion);
}

我有一个长时间运行的函数ProcessEachMachine。在继续函数UpdateLabel中,我想访问UIlabel并更新状态。

private void UpdateLabel()
{
   progressLbl.Text = "updated";
}

但是标签没有更新。如何访问UILabel并更新其文本?

你确定 ProcessEachMachine 在更新标签之前已经完成了吗?也许这个任务正在运行很长时间,非常长的时间。 - GETah
1
ContinueWith是什么保证了呢?我假设错了吗? - Sandeep
@Sandeep 不,你没有错。你只需要确保回到UI线程即可。 - Servy
1个回答

10

你必须在ContinueWith上设置TaskScheduler.FromCurrentSynchronizationContext,否则它将不会在UI上下文中运行。这是MSDN关于你必须使用的ContinueWith调用的重写方法

最终应该看起来像这样:

.ContinueWith(UpdateLabel, null, 
    TaskContinuationOptions.OnlyOnRanToCompletion,
    TaskScheduler.FromCurrentSynchronizationContext());

虽然看起来什么也没有发生,但TPL当前正在吞噬您的跨线程异常。如果您不打算检查每个结果或检查其异常,则应该使用UnobservedTaskException。否则,在进行垃圾回收时,异常将在那时发生……这可能会导致难以调试的错误。
更新
根据您有关主任务由Backgroundworker设置和启动的更新,我的主要问题是为什么不能使用任务来启动?实际上,如果Method中没有更多内容,那么这只是重复劳动,可能会混淆其他开发人员。您已经异步启动了,那么为什么不在后台工作器中完成工作,并使用一个OnComplete方法来UpdateLabel(因为后台工作器已经具有上下文感知)。
但主要问题仍然存在,因此如果您必须使用TPL,则以下是一些其他解决方案:
  1. 您可以在UpdateLabel方法中使用Invoke返回到主UI线程。
  2. 您可以将当前上下文传递到后台工作器中,并使用该上下文。
  3. 您可以Wait等待原始任务返回,然后使用工作程序的oncomplete事件更新标签。

以下是我会如何做(所有伪代码)

Background Worker Method:

Method() called because of Background worker

private void Method()
{
    fileProcessor.ProcessEachMachine(mdetail);
}

Wire up background worker's OnRunWorkerCompleted:

if(!e.Cancelled && !e.Error)
    UpdateLabel();

仅限任务方法

Call Method() from the main thread and just let the TPL do its work :)

Task.Factory.StartNew(() => fileProcessor.ProcessEachMachine(mdetail))
       .ContinueWith((precedingTask)=>{if(!precedingTask.Error)UpdateLabel;}, 
           null, TaskContinuationOptions.OnlyOnRanToCompletion,
           TaskScheduler.FromCurrentSynchronizationContext());

对不起,我已经纠正了问题。 - Sandeep
我之所以这样做是因为我需要连接到数据库,获取机器列表,然后搜索所有的10台机器。如果有更好的方法,我很愿意学习。 - Sandeep
@Sandeep 我提供了我的建议,基于您接受答案的情况,我会认为这已经足够解释了。但是,如果还不清楚,我可以提供更详细的示例,以便更加清晰地说明如何更好地完成此操作。 - Justin Pihony
@Sandeep,我已经更新了我的答案,并添加了一些伪代码,这可能有助于澄清事情? - Justin Pihony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接