WinForms TPL Pattern w/ Multiple Tasks & UI Sync - is this correct?

微笑、不失礼 提交于 2019-12-07 18:32:49

问题


I'm new to the TPL (Task-Parallel Library) and am wondering if the following is the most efficient way to spin up 1 or more tasks, collate the results, and display them in a datagrid.

  1. Search1 & Search2 talk to two separate databases, but return the same results.
  2. I disable the buttons and turn on a spinner.
  3. I'm firing the tasks off using a single ContinueWhenAll method call.
  4. I've added the scheduler to the ContinueWhenAll call to update form buttons, datagrid, and turn off the spinner.

Q: Am I doing this the right way ? Is there a better way ?
Q: How could I add cancellation/exception checking to this ?
Q: If I needed to add progress reporting - how would I do that ?

The reason that I chose this method over say, a background worker is so that I could fire each DB task off in parallel vs. sequentially. Besides that, I thought it might be fun to use the TPL.. however, since I could not find any concrete examples of what I'm doing below (multiple tasks) I thought it might be nice to put it on here to get the answers, and hopefully be an example for others.

Thank you!

Code:

//  Disable buttons and start the spinner
btnSearch.Enabled = btnClear.Enabled = false;
searchSpinner.Active = searchSpinner.Visible = true;

//  Setup scheduler
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();

//  Start the tasks
Task.Factory.ContinueWhenAll(
  //  Define the search tasks that return List<ImageDocument>
  new [] {  
    Task.Factory.StartNew<List<ImageDocument>>(Search1), 
    Task.Factory.StartNew<List<ImageDocument>>(Search2) 
  }, 
  //  Process the return results
  (taskResults) => {
    //  Create a holding list
    List<ImageDocument> documents = new List<ImageDocument>();
    //  Iterate through the results and add them to the holding list
    foreach (var item in taskResults) {
      documents.AddRange(item.Result);
    }
    //  Assign the document list to the grid
    grid.DataSource = documents;
    //  Re-enable the search buttons
    btnSearch.Enabled = btnClear.Enabled = true;
    //  End the spinner
    searchSpinner.Active = searchSpinner.Visible = false;
  }, 
  CancellationToken.None, 
  TaskContinuationOptions.None, 
  scheduler
);

回答1:


Q: Am I doing this the right way ? Is there a better way ?

Yes, this is a good way to handle this type of situation. Personally, I would consider refactoring the disable/enable of the UI into a separate method, but other than that, this seems very reasonable.

Q: How could I add cancellation/exception checking to this ?

You could pass around a CancellationToken to your methods, and have them check it and throw if a cancellation was requested.

You'd handle exceptions where you grab the results from taskResults. This line:

  documents.AddRange(item.Result);

Is where the exception will get thrown (as an AggregateException or OperationCanceledException) if an exception or cancellation occurred during the operations.

Q: If I needed to add progress reporting - how would I do that ?

The simplest way would be to pass the scheduler into your methods. Once you've done that, you could use it to schedule a task that updates on the UI thread - ie: Task.Factory.StartNew with the TaskScheduler specified.


however, since I could not find any concrete examples of what I'm doing below (multiple tasks)

Just FYI - I have samples of working with multiple tasks in Part 18 of my series on TPL.




回答2:


For best practices, read the Task-Based Asynchronous Pattern document. It includes recommendations on cancellation support and progress notification for Task-based APIs.

You'd also benefit from the async/await keywords in the Async CTP; they greatly simplifiy task continuations.



来源:https://stackoverflow.com/questions/6765046/winforms-tpl-pattern-w-multiple-tasks-ui-sync-is-this-correct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!