I was reading a book on Parallel Programming and I coded the following scenario which worked as expected (Task.Status == TaskStatus.Canceled):
It’s possible for a task to end in TaskStatus.Canceled. For this to occur, you must pass a CancellationToken as an argument to the factory method that created the task. If that token signals a request for cancellation before the task begins to execute, the task won’t be allowed to run. The task’s Status property will transition directly to TaskStatus.Canceled without ever invoking the task’s user delegate.
But I couldn't get expected result (Task.Status != TaskStatus.Canceled) for the following:
If the token signals a cancellation request after the task begins to execute, the task’s Status property will only transition to TaskStatus.Canceled if the user delegate throws an Operation CanceledException and that exception’s CancellationToken property contains the token that was given when the task was created.
Code
public void DoWork(int millisec, CancellationToken token, int n )
{
ParallelOptions option = new ParallelOptions { CancellationToken = token };
Parallel.For(0, n, option, (i) =>
{
// Thread.Sleep(millisec);
token.ThrowIfCancellationRequested();
});
}
private void btnSearch_Click(object sender, EventArgs e)
{
CancellationToken token = cts.Token;
t1 = Task.Factory.StartNew(() => DoWork(700, token, 99999999), token);
t2 = Task.Factory.StartNew(() => DoWork(5, token, 1), token);
Task[] tasks = new Task[] { t1,t2 };
int completedTaskIndex = Task.WaitAny(tasks);
lblMsg.Text = "Task No." + ++completedTaskIndex + " has been completed.\n";
cts.Cancel();
Thread.Sleep(5000); // Updated
lblMsg.Text += "Rest of the tasks have been cancelled.\n";
try
{
lblMsg.Text += "Task 1 Status: " + t1.Status + "\n";
lblMsg.Text += "Task 2 Status: " + t2.Status + "\n";
Task.WaitAll(tasks);
lblMsg.Text += "No exceptions has been reported yet.\n";
}
catch(AggregateException ae)
{
ae.Flatten().Handle((ex) =>
{
// if you debug to see the token handle it is the same as passed to Task.Factory.StartNew()
if (ex is OperationCanceledException)
{
CancellationToken tok = ((OperationCanceledException)ex).CancellationToken;
return true;
}
else
{
return false;
}
}
);
}
finally
{
if(cts!= null)
cts.Dispose();
cts = null;
}
}
Result
Before Update:
After Update:
Dicussion
Task 1 is the task which is cancelled so its Status should have been Cancelled instead of Running as shown in figure above.
Question
Is there any understanding gap that I could not get desired result? Where did i lose the track?
Updated (Issue Resolved)
The problem was that I was reading the status of task 1 before it was updated to Cancelled. So I wrote Thread.Sleep(5000); just after cts.Cancel(); so that there's enough time for the task to be cancelled and the status be updated appropriately. That let the status of task 1 to show "Cancelled".