从 Task.Continuewith 更新 UI 标签

本文关键字:UI 标签 更新 Continuewith Task | 更新日期: 2023-09-27 18:32:10

我正在开发一个Winform应用程序。Method后台工作线程启动。对不起。我之前没有提到这一点。

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并更新其文本。?

从 Task.Continuewith 更新 UI 标签

您必须

在 ContinueWith 上设置 TaskScheduler.FromCurrentSynchronizationContext,否则它将无法在 UI 上下文中运行。下面是重写上的 MSDN,您必须使用该替代来调用以继续使用。

它最终应该看起来像这样:

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

看起来好像什么都没有发生,但 TPL 目前正在吞噬您的交叉线程异常。如果您不打算检查每个结果或检查其异常,则可能应该使用 UnobservedTaskException。否则,当发生垃圾回收时,将发生异常...这可能会产生难以调试的错误。

更新

根据您对正在设置并由后台工作者启动的主任务的更新,我的主要问题是为什么这无法使用任务启动?事实上,如果Method中没有更多,那么这实际上只是双重工作,可能会使其他开发人员感到困惑。您已经异步启动了,那么为什么不直接在后台工作线程中完成工作并使用将UpdateLabel的 OnComplete 方法(因为后台工作线程已经可以感知上下文)。

不过,主要问题仍然相同,因此,如果您觉得必须使用 TPL,这里有一些其他解决方案:

  1. 您可以在 UpdateLabel 方法中Invoke回到主 UI 线程
  2. 您可以将当前上下文传递到后台工作线程中并改用它
  3. 您可以Wait返回原始任务,然后使用工作人员的 oncomplete 事件更新标签。

这是我将如何做到这一点(所有伪代码)

后台工作线程方法:

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());