AttachedToParent Task confusion - c#

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.

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.

Related

Extending C# async/await await doesn't await

Extending: C# async/await await doesn't await
In case the methods executed sequentially are stored in a list as well, how can I add the ExecuteParallelAsync to that list?
private async Task ExecuteSequential()
{
List<Action> sequentialMethods = new List<Action>()
{
SomeMethod1,
SomeMethod2,
await ExecuteParallelAsync, // ???
SomeMethod3,
SomeMethod4
};
for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
{
sequentialMethods.ElementAt( i ).Invoke();
}
}
Clarification:
private async Task ExecuteParallelAsync()
{
List<Action> methods = new List<Action>()
{
MyMethod1,
MyMethod2,
MyMethod3
};
await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );
}
sequentialMethods is a List<Action> but ExecuteParallelAsync is NOT an Action. I've tried to split the list, like suggested. Unfortunately, it doesn't help.
The way suggested by Marc from the original code works fine. But I want to do some extra stuff after each (sequential) method call, which is why I'm trying to use a list and a loop instead of plain method calls.
But when I do so, I'm facing the original problem again, that SomeMethod3 is executed before ExecuteParallelAsync is finished.
Once again, everything in ExecuteParallelAsync can and should be executed simultaneously. Everything in ExecuteSequential has to be executed sequentially.
Solution:
Gabriel is absolutely correct. The crucial statement is this
await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );
When I remove every async and every Task, so that everything is intended to run be executed sequentially this line is the key:
Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );
When I use this last statement, everything works correctly.
Thanks to everyone, all your ideas, thoughts and efforts helped and are appreciated.
I'm facing the original problem again, that SomeMethod3 is executed before ExecuteParallelAsync is finished
Then this is not a use case for asynchronous programming. Your requirement is that this is synchronous.
That is especially so since you said that MyMethod1/MyMethod2/MyMethod3 are not asynchronous methods. If they were, that would be a totally different thing. But since they aren't, I don't see any value in trying to use async and await here.
But don't confuse asynchronous with parallel. It seems you want the methods called in ExecuteParallelAsync to be run in parallel, that's fine. You just don't need async and await.
For example:
private void ExecuteSequential()
{
List<Action> sequentialMethods = new List<Action>()
{
SomeMethod1,
SomeMethod2,
ExecuteParallel,
SomeMethod3,
SomeMethod4
};
for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
{
sequentialMethods.ElementAt( i ).Invoke();
}
}
private void ExecuteParallel()
{
List<Action> methods = new List<Action>()
{
MyMethod1,
MyMethod2,
MyMethod3
};
Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );
}
You can create an Action that when invoked will start the task and wait for it to complete, like this:
List<Action> sequentialMethods = new List<Action>()
{
SomeMethod1,
SomeMethod2,
() => ExecuteParallelAsync().Wait(),
SomeMethod3,
SomeMethod4
};
Here's a version:
private async Task ExecuteSequential()
{
var sequentialMethods = new List<Func<Task>>()
{
() => Task.Run(SomeMethod1),
() => Task.Run(() => SomeMethod2("Hey!")),
ExecuteParallelAsync,
() => Task.Run(SomeMethod3),
() => Task.Run(SomeMethod4)
};
for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
{
Task t = sequentialMethods[i].Invoke();
await t;
// or just await sequentialMethods[i]();
}
}
Just for the record, ExecuteSequential could just be a standard async method (which are very good in executing things sequentially).
private async Task ExecuteSequential()
{
SomeMethod1();
SomeMethod2();
await ExecuteParallelAsync();
SomeMethod3();
SomeMethod4();
};
Edit: Test
Code
class Program
{
public void MyMethod1() => Console.WriteLine("||My1");
public void MyMethod2() => Console.WriteLine("||My2");
public void MyMethod3() => Console.WriteLine("||My3");
private async Task ExecuteParallelAsync()
{
Console.WriteLine("||Start");
List<Action> methods = new List<Action>() { MyMethod1, MyMethod2, MyMethod3 };
await Task.Run(() => { Parallel.ForEach(methods,
(currentMethod) => currentMethod.Invoke()); }); // This could be just 'currentMethod();' (no Invoke())
Console.WriteLine("||End");
}
public void SomeMethod1() => Console.WriteLine("Some1");
public void SomeMethod2(string s) => Console.WriteLine($"Some2: {s}");
public void SomeMethod3() => Console.WriteLine("Some3");
public void SomeMethod4() => Console.WriteLine("Some4");
private async Task ExecuteSequential()
{
var sequentialMethods = new List<Func<Task>>()
{
() => Task.Run(SomeMethod1),
() => Task.Run(() => SomeMethod2("Hey!")),
ExecuteParallelAsync,
() => Task.Run(SomeMethod3),
() => Task.Run(SomeMethod4)
};
for (int i = 0; i < sequentialMethods.Count; i++)
{
await sequentialMethods[i]();
}
}
static async Task Main(string[] args)
{
await new Program().ExecuteSequential();
Console.WriteLine("All done");
}
}
Output
Some1
Some2: Hey!
||Start
||My3
||My2
||My1
||End
Some3
Some4
All done
My1, My2, and My3 will change order between executions, but always within ||Start and ||End.

Wait all tasks with some conditions

I am writing some complex tasks and I can't do success to solve the problem.
I open some tasks for searching some data asynchronously.
At the end I wait for all tasks.
I want to run on all the data and does not stop the tasks until they all finish.
But, when one of the tasks finds some relevant data, I want to continue with the code that is after the wait all tasks, but at the same time, I want to continue searching with my tasks (until all the data will be read).
Here I found a way how to continue the code after the wait all if one of the tasks finds the data, the problem is that it also stops all tasks from running.
Here is my code (this is just code for demonstration of the problem).
private static void RunTasks()
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Task<bool> t0 = Task.Factory.StartNew<bool>(() => Find(1, 2));
Task<bool> t1 = Task.Factory.StartNew<bool>(() => Find(4, 7));
Task<bool> t2 = Task.Factory.StartNew<bool>(() => Find(13, 14));
t0.ContinueWith(_ =>
{
if (t0.Result)
tcs.TrySetResult(t0.Result);
});
t1.ContinueWith(_ =>
{
if (t1.Result)
tcs.TrySetResult(t1.Result);
});
t2.ContinueWith(_ =>
{
if (t2.Result)
tcs.TrySetResult(t2.Result);
});
tcs.Task.Wait();
Console.WriteLine("Found");
ContinueWork(); //Runs after at least one data found or when all tasks finish.
}
//Just for demonstration...
private static bool Find(int a, int b)
{
Console.WriteLine("a: " + a + " b: " + b);
return a == 4 && b == 7 ? true : false;
}
How can I write it so that when some data is found it will continue to ContinueWork method and also will continue with reading the data with the tasks?
Thanks.
This is not the cleanest way but it will serve your purposes:
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Task<bool>[] tasks = new Task<bool>[3];
tasks[0] = Task.Factory.StartNew<bool>(() => Find(1, 2));
tasks[1] = Task.Factory.StartNew<bool>(() => Find(4, 7));
tasks[2] = Task.Factory.StartNew<bool>(() => Find(13, 14));
tasks[0].ContinueWith(_ =>
{
if (tasks[0].Result)
tcs.TrySetResult(tasks[0].Result);
});
tasks[1].ContinueWith(_ =>
{
if (tasks[1].Result)
tcs.TrySetResult(tasks[1].Result);
});
tasks[2].ContinueWith(_ =>
{
if (tasks[2].Result)
tcs.TrySetResult(tasks[2].Result);
});
Task.WaitAny(tasks);
Console.WriteLine("Found");
ContinueWork();

Switch async Task to sync task

I have the following code:
Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
Is it possible to convert ContinueWhenAll to a synchronous method? I want to switch back between async and sync.
Edit: I should metnion that each of the "tasks" in the continuewhenall method should be executing synchronously.
If you want to leave your existing code intact and have a variable option of executing synchronously you should make these changes:
bool isAsync = false; // some flag to check for async operation
var batch = Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
if (!isAsync)
batch.Wait();
This way you can toggle it programmatically instead of by editing your source code. And you can keep the continuation code the same for both methods.
Edit:
Here is a simple pattern for having the same method represented as a synchronous and async version:
public Item CreateItem(string name)
{
return new Item(name);
}
public Task<Item> CreateItemAsync(string name)
{
return Task.Factory.StartNew(() => CreateItem(name));
}
Unless am mistaken this is what you're looking for
Task.WaitAll(tasks);
//continuation code here
i think you can try this.
using TaskContinuationOptions for a simple scenario.
var taskFactory = new TaskFactory(TaskScheduler.Defau
var random = new Random();
var tasks = Enumerable.Range(1, 30).Select(p => {
return taskFactory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Console.WriteLine(#"* 2: ID = " + t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}).ToArray();
Task.WaitAll(tasks);
or using TPL Dataflow for a complex scenario.
var step2 = new ActionBlock<int>(i => {
Thread.Sleep(i);
Console.WriteLine(#"* 2: ID = " + i);
}, new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism = 1,
//MaxMessagesPerTask = 1
});
var random = new Random();
var tasks = Enumerable.Range(1, 50).Select(p => {
return Task.Factory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Thread.Sleep(t.Result);
step2.Post(t.Result);
});
}).ToArray();
await Task.WhenAll(tasks).ContinueWith(t => step2.Complete());
await step2.Completion;

Sequence .Net Tasks

I have several Task objects, such as Task<int>, Task<string>, Task<double>, that I want to run sequentially. That is, as each Task completes the next Task starts automatically. Each task depends on the output from the prior task.
How do I do this? The myTask.ContinueWith(...) overloads all assume a delegate parameter. But a Task is not a delegate.
You could do like:
var t1 = new Task(() => Console.WriteLine("Completed t1"));
var t2 = new Task(() => Console.WriteLine("Completed t2"));
var t3 = new Task(() => Console.WriteLine("Completed t3"));
t1.ContinueWith(t => t2.RunSynchronously())
.ContinueWith(t => t3.RunSynchronously());
t1.Start();
The example above only works, if you don't care about the results of the tasks. If you however (as your edit states), need to pass the result of each task to the next task, then you could do something like:
var t1 = new Task(() => Console.WriteLine("Completed t1"));
var t2 = new Task(() => Console.WriteLine("Completed t2"));
var t3 = new Task(() => Console.WriteLine("Completed t3"));
t1.ContinueWith(task1 =>
{
Console.WriteLine(task1.Result);
t2.ContinueWith(task2 =>
{
Console.WriteLine("{0} | {1}", task1.Result, task2.Result);
t3.ContinueWith(task3 =>
{
Console.WriteLine("{0} | {1} | {2}",
task1.Result, task2.Result, task3.Result);
});
t3.Start();
});
t2.Start();
});
t1.Start();
/* OUTPUT:
Completed t1
Completed t1 | Completed t2
Completed t1 | Completed t2 | Completed t3 */

Update UI from Task Parallel Library issue

I am learning the art of TPL. So I try to update UI in incremental fashion from TPL but I can't get it right. Here is the code I'm using:
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
//System.Threading.Thread.SpinWait(50000000);
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.Sleep(1000);
}
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
I am trying to update windows title bar UI in incremental fashion. The code runs without exceptions but the UI is not updated until i's value become 5. I want to show it like; 1, then 2 and so on until i is 5.
When I am running my program I noticed that my program hangs until the task is completed or finished.
What am I doing wrong in respect of updating the UI when using tasks?
another approach 1
Task.Factory.StartNew(() =>
{
Parallel.For(1, 6, i =>
{
System.Threading.Thread.SpinWait(500000000); do work here
BeginInvoke((Action)delegate { this.Text = i.ToString(); });
});
});
another approach 2
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.SpinWait(50000000); // do work here
}
}); // <---- Removed arguments (specifically uiScheduler)
You're telling the task to use the uiScheduler context when starting the initial task. Remove that and your code should be fine.
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.Sleep(1000);
}
}); // <---- Removed arguments (specifically uiScheduler)

Categories

Resources