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 (outputting numbers 1-1000). I put a substantial pause in half way through to simulate a long running process. While this long pause is taking place, if I hit the Stop button, its event handler calls the Cancel method of CancellationTokenSource. I don't want to do any further processing (in this case, outputting a message) in the Stop button's event handler until the canceled task is done with its current iteration. How do I do this? I tried using Task.WaitAll, etc in the Stop button's event handler, but that just throws an unhandled AggregateException. Here's the code which will help explain my problem if run as described above:
private Task t; private CancellationTokenSource cts; public Form1() { InitializeComponent(); } private void startButton_Click(object sender, EventArgs e) { statusTextBox.Text = "Output started."; // Create the cancellation token source. cts = new CancellationTokenSource(); // Create the cancellation token. CancellationToken ct = cts.Token; // Create & start worker task. t = Task.Factory.StartNew(() => DoWork(ct), ct); } private void DoWork(CancellationToken ct) { for (int i = 1; i <= 1000; i++) { ct.ThrowIfCancellationRequested(); Thread.Sleep(10); // Slow down for text box outout. outputTextBox.Invoke((Action)(() => outputTextBox.Text = i + Environment.NewLine)); if (i == 500) { Thread.Sleep(5000); } } } private void stopButton_Click(object sender, EventArgs e) { cts.Cancel(); Task.WaitAll(t); // this doesn't work :-( statusTextBox.Text = "Output ended."; } private void exitButton_Click(object sender, EventArgs e) { this.Close(); }
Any help with this would be greatly appreciated. Thanks in advance.
Instead of canceling your running task and catching exception to show user what has really happened, you can continue with another task only if the previous task has been cancelled. Make use of Continue With method which takes the returned value from the cancelled task as a parameter.
Wait is a synchronization method that causes the calling thread to wait until the current task has completed. If the current task has not started execution, the Wait method attempts to remove the task from the scheduler and execute it inline on the current thread.
Create and start a cancelable task. Pass a cancellation token to your user delegate and optionally to the task instance. Notice and respond to the cancellation request in your user delegate. Optionally notice on the calling thread that the task was canceled.
There's 2 likely reasons that a TaskCanceledException would be thrown: Something called Cancel() on the CancellationTokenSource associated with the cancellation token before the task completed. The request timed out, i.e. didn't complete within the timespan you specified on HttpClient. Timeout .
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(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With