I have a non-intuitive issue with a continuation task that I would think task.Wait would wait for the continuation task but it yields RanToCompletion
before the task even finishes running? Here is the short source code. The output is below:
private static void TestChildTasks()
{
Task t = Task.Run(() => RunParentTask());
Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));
//Task t2 = Task.Run(() => RunChildTask());
Console.WriteLine("Waiting on t1");
t.Wait();
Console.WriteLine("Done waiting on t1");
Console.WriteLine($"Waiting on t2, status of {t2.Status}");
t2.Wait();
Console.WriteLine($"Finished; child task is {t2.Status}");
}
private static void RunParentTask()
{
Console.WriteLine("Parent Task is running");
Thread.Sleep(2000);
Console.WriteLine("Parent Task is done");
}
private static void RunChildTask()
{
Console.WriteLine("Child task is running");
Thread.Sleep(3000);
Console.WriteLine("Child Task is done");
}
Here is the output:
Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Waiting on t2, status of Running
Finished; child task is RanToCompletion
press enter to exit
Child task is running
Child Task is done
Why does the child task continue to run after it returns a status of RanToCompletion
?
Lets look at the following line:
Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));
Even though you have declared t2
as Task
it is really a Task<Task>
:
Task<Task> t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));
Why? Because Task.Run
creates a new Task
.
You have several options to fix this:
To make a Task
from a Task<Task>
you need to call the Unwrap
method.
Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask())).Unwrap();
With this modification the output will look like this:
Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2, status of WaitingForActivation
Child Task is done
Finished; child task is RanToCompletion
Actually you can call the RunChildTask
without the Task.Run
:
Task t2 = t.ContinueWith(_ => RunChildTask());
With this modification the output will look like this:
Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2, status of Running
Child Task is done
Finished; child task is RanToCompletion
If you want to you can attach a Task not just with the ContinueWith
, but also with the Task.Factory.StartNew
:
Task t2 = null;
Task t = Task.Factory.StartNew(() =>
{
RunParentTask();
t2 = Task.Factory.StartNew(RunChildTask, TaskCreationOptions.AttachedToParent);
});
With this modification the output will look like this:
Waiting on t1
Parent Task is running
Parent Task is done
Child task is running
Child Task is done
Done waiting on t1
Waiting on t2, status of RanToCompletion
Finished; child task is RanToCompletion
There are several other ways as well.