从Task.Continuewith更新UI标签

San*_*eep 4 .net c#

我正在开发一个Winform应用程序.它MethodBackgroundWorker Thread 启动.对不起.我之前没有提到这一点.

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

我有一个长期运行的功能ProcessEachMachine.在延续函数中,UpdateLabel我想访问UIlabel并更新状态.

private void UpdateLabel()
{
   progressLbl.Text = "updated";
}
Run Code Online (Sandbox Code Playgroud)

但标签没有得到更新.如何访问UILabel并更新它的文本.

Jus*_*ony 8

您必须在ContinueWith 上设置TaskScheduler.FromCurrentSynchronizationContext,否则它将不会在UI上下文中运行.以下是对此次ContinueWith调用必须使用的覆盖的MSDN.

它应该看起来像这样:

.ContinueWith(UpdateLabel, null, 
    TaskContinuationOptions.OnlyOnRanToCompletion,
    TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

似乎没有任何事情发生,但TPL目前正在吞噬您的跨线程异常.如果您不打算检查每个结果或检查其异常,则应该使用UnobservedTaskException.否则,当发生垃圾收集时,会发生异常......这可能会导致难以调试的错误.

UPDATE

根据您关于正在设置并由Backgroundworker启动的主要任务的更新,我的主要问题是为什么这不能使用任务启动?事实上,如果没有更多Method,那么这实际上只是双重工作,可能会混淆其他开发人员.你已经异步启动了,所以为什么不在backgroundworker中做你的工作并使用一个OnComplete方法UpdateLabel(因为后台工作者已经知道了上下文).

主要问题仍然是相同的,所以如果您觉得必须使用TPL,这里有一些其他解决方案:

  1. 您可以Invoke在UpdateLabel方法中返回主UI线程
  2. 您可以将当前上下文传递给后台工作程序并使用它
  3. 您可以Wait返回原始任务,然后使用worker的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();
Run Code Online (Sandbox Code Playgroud)

仅任务方法

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());
Run Code Online (Sandbox Code Playgroud)