4

我正在开发一个 Winform 应用程序。由BackgroundWorker线程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 并更新它的文本。?

4

1 回答 1

9

You have to set the TaskScheduler.FromCurrentSynchronizationContext on ContinueWith or else it will not be run in the UI context. Here is the MSDN on the override that you must use for this call to ContinueWith.

It should end up looking like this:

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

It may seem like nothing is happening, but the TPL is currently swallowing your cross thread exception. You should probably use the UnobservedTaskException if you are not going to inspect each result or check for its exception. Otherwise, when garbage collection occurs, the exception will happen then...which could create hard to debug errors.

UPDATE

Based on your update about the main Task being setup and started by a Backgroundworker, my main question is why this could not use a Task to start? In fact, if there is not more in the Method, then this is really just double work and might confuse other developers. You are already started asynchronously, so why not just do your work within the backgroundworker and use an OnComplete method that will UpdateLabel (as background workers are already context aware).

The main problem is still the same though, so here are some other solutions if you feel you must use the TPL:

  1. You can Invoke back onto the main UI thread within the UpdateLabel method
  2. You can pass the current context into the backgroundworker and use that instead
  3. You can Wait for your original Task to return and then use the worker's oncomplete event to update the label.

Here is how I would do this (all pseudo code)

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

Task only method

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());
于 2012-04-23T18:15:26.120 回答