I'm trying to understand the behavior of the .net Tasks, when children is attached.
I have the following test code:
void Test()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
}, token);
Thread.Sleep(500);
Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
Debug.WriteLine("State of child before cancel is {0}", child.Status);
tokenSource.Cancel();
Thread.Sleep(500);
Debug.WriteLine("State of parent is {0}", parent.Status);
Debug.WriteLine("State of child is {0}", child.Status);
}
Result of this is:
State of parent before cancel is WaitingForChildrenToComplete
State of child before cancel is Running
A first chance exception of type 'System.OperationCanceledException' occurred in mscorlib.dll
State of parent is RanToCompletion
State of child is Canceled
Aparrently the parent task state is not Canceled
, even though
both tasks share the token, and child is attached.
How do i make the parent task return state Canceled
when a cancellation occurs?
NOTE
If i throw an exception both tasks return Faulted
.
This is expected behavior as stated on MSDN. The parent task has to wait (scroll down to the cancellation section) for the child task. The parent task has to handle all benign faults (like cancellation).
To make your parent task fail, just wait and pass the token:
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested) Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
// This is the magic line.
child.Wait(token);
}, token);
If you are using this code to do something productive and not only for testing, you should also consider using the simplified Task.Run()
which supports async
delegates instead of Task.Factory.StartNew()
. This article is very interesting.
Your example is quite convoluted and hides the intuitive behavior and your expectations are wrong.
Let's start from the working example:
void Test()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token);
Thread.Sleep(500);
Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
Debug.WriteLine("State of child before cancel is {0}", child.Status);
tokenSource.Cancel();
Thread.Sleep(500);
Debug.WriteLine("State of parent is {0}", parent.Status);
Debug.WriteLine("State of child is {0}", child.Status);
}
In order for the parent to be canceled, you need to call somewhere in the body of the parent token.ThrowIfCancellationRequested()
. However, just calling token.ThrowIfCancellationRequested()
won't suffice.
You need to conceptualize how parent
and child
execution flows go together in order to see why your expectations are wrong.
Main thread: ---\------------------------------------[Cancel]-----/
Parent: \---\-----[Check cancellation]------------------/
Child: \------------------------------[Cancel]---/
As you can see from the diagram above, the parent checks for cancellation way before the cancellation is requested. The child receives the cancellation signal because it basically waits for a cancellation to be triggered. Now, if you put the same mechanism in the parent it will receive the cancellation signal because it wouldn't have finished it's job prior to the cancellation being signaled.
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