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