I obviously don\'t know what I\'m doing when it comes to parallel programming with .NET 4.0. I have a simple Windows app that starts a task to do some mindless work (outputt
You would normally just use Task.Wait
(instead of WaitAll
), as it's a single task. and then handled the exception appropriately:
private void stopButton_Click(object sender, EventArgs e)
{
cts.Cancel();
try
{
t.Wait(); // This will throw
}
catch (AggregateException ae)
{
ae.Handle<OperationCanceledException>(ce => true);
}
statusTextBox.Text = "Output ended.";
}
When you cancel a Task
, the OperationCanceledException
will get wrapped into an AggregateException
and be thrown as soon as you call Wait()
or try to get the Task's Result
(if it's a Task<T>
).
Purely for your information - This is one place, especially given what you're doing here, where C# 5 simplifies things. Using the new async support, you can write this as:
// No need for "t" variable anymore
// private Task t;
private async void startButton_Click(object sender, EventArgs e)
{
statusTextBox.Text = "Output started.";
// Create the cancellation token source.
cts = new CancellationTokenSource();
try
{
// Create & start worker task.
await Task.Run(() => DoWork(cts.Token));
statusTextBox.Text = "Output ended.";
}
catch(OperationCanceledException ce)
{
// Note that we get "normal" exception handling
statusTextBox.Text = "Operation canceled.";
}
}
private void stopButton_Click(object sender, EventArgs e)
{
// Just cancel the source - nothing else required here
cts.Cancel();
}
Sorry, not enough reputation to just add a question as a comment to that answer. I wanted to ask how a work-around is considered an answer.
Doesn't the code in the answer rely on some sort of user interface tweak to keep the user from starting more than one background process at a time? That's always a problem, of course.
Here, I present an alternative that answer the question that is stated. It shows how to wait for the cancellation request to finish. It does this in a way that still lets the UI screw things up if it is not managed well, but has code that actually waits after the cancellation, if that is what is really needed. This is an excerpt from a larger C# class:
AutoResetEvent m_TaskFinishedEvent = new AutoResetEvent( false );
private IAsyncAction m_Operation = null;
private Task WaitOnEventAsync( EventWaitHandle WaitForEvent )
{
return Task.Run( () => { WaitForEvent.WaitOne(); } );
}
public async void Start()
{
if( m_Operation != null )
{
// Cancel existing task
m_Operation.Cancel();
// Wait for it to finish. This returns control to the UI.
await WaitOnEventAsync( m_TaskFinishedEvent );
}
// Start the background task.
m_Operation = ThreadPool.RunAsync( WorkerMethod );
}
private void WorkerMethod( IAsyncAction operation )
{
while( m_Operation.Status != AsyncStatus.Canceled )
; // Add real work here.
m_TaskFinishedEvent.Set();
}
This code relies on an event object to signal that the task is mostly finished. The WorkerMethod() code has not returned yet, but all useful work is done when the event is signaled.
I did not provide a Stop() function because of how I use this code. The code to do the wait would just go in that Stop() function if that was how the code needs to work.
Yes, you cannot use a regular Wait() function because of the cancellation exception. But the accepted answer is more of a work-around, no offense (and maybe I'm wrong?).