AttachedToParent Task confusion
c#, console-application, multithreading, task-parallel-library
Solution
Look at this blog post: Task.Run vs Task.Factory.StartNew
The first example:
Task.Run(someAction);
is simplified equivalent of method:
Task.Factory.StartNew(someAction,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
I made little research, using reflector, here is a source of method `Task.Run`
public static Task Run(Func<Task> function, CancellationToken cancellationToken)
{
if (function == null)
throw new ArgumentNullException("function");
cancellationToken.ThrowIfSourceDisposed();
if (cancellationToken.IsCancellationRequested)
return Task.FromCancellation(cancellationToken);
else
return (Task) new UnwrapPromise<VoidTaskResult>(
(Task) Task<Task>.Factory.StartNew(function,
cancellationToken,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default),
true);
}
The important parameter of method `Task.Factory.StartNew` is `TaskCreationOptions creationOptions`. In method `Task.Factory.StartNew` that parameter equals `TaskCreationOptions.DenyChildAttach`. It mean that
an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task
You need to change to `TaskCreationOptions.None` to achieve right behavior of code.
Method `Task.Run` does not provide ability to change TaskCreationOptions parameter.
Problem
I have a problem understanding how `AttachedToParent` parameter works. Here is the sample code: ``` public static void Main(string[] args) { Task<int[]> parentTask = Task.Run(()=> { int[] results = new int[3]; Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent); Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent); Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent); t1.Start(); t2.Start(); t3.Start(); return results; }); Task finalTask = parentTask.ContinueWith(parent => { foreach (int result in parent.Result) { Console.WriteLine(result); } }); finalTask.Wait(); Console.ReadLine(); } ``` As I understand, when a Task has child Tasks, the parent Task finishes when all the child tasks are ready. The problem with this example is that the output looks like this: ``` 0 0 0 ``` This means that the parent Task was not waiting for its child tasks to finish. The only way to get a valid result `0 1 2` is to use Wait on all of the child Taks, by adding some piece of code like this before the `return results;` statement: ``` Task[] taskList = { t1, t2, t3 }; Task.WaitAll(taskList); ``` My question is this. Why do we use `TaskCreationOptions.AttachedToParent` when we also have to manually call Wait method for every child Task? Edit: While I was writing this question, I've changed code a little bit and now AttachedToParent works well. The only difference is that I've used `parentTask.Start();` instead of the `Task.Run();`. ``` public static void Main(string[] args) { Task<int[]> parentTask = new Task<int[]>(()=> { int[] results = new int[3]; Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent); Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent); Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent); t1.Start(); t2.Start(); t3.Start(); //Task[] taskList = { t1, t2, t3 }; //Task.WaitAll(taskList); return results; }); parentTask.Start(); Task finalTask = parentTask.ContinueWith(parent => { foreach (int result in parent.Result) { Console.WriteLine(result); } }); finalTask.Wait(); Console.ReadLine(); } ``` I still don't understand why there is a problem with the first example.