Task.Factory.ContinueWhenAll with async continuationAction - c#

What is the right way to link multiple asynchronous tasks together? I want to know that all tasks have succeeded, or that one task failed, and that my "pipeline" failed
Task task1 = RunTask1();
Task task2 = RunTask2();
Task task3_isDependentOnTasks1And2 = Task.Factory.ContinueWhenAll(
new[] {task1,task2},
async tasks => await RunTask3());
My experience debugging the code says that RunTask3 never gets awaited.
On the other hand this
Task task1 = RunTask1();
Task task2 = RunTask2();
Task task3_isDependentOnTasks1And2 = Task.Factory.ContinueWhenAll(
new[] {task1,task2},
tasks => RunTask3().GetAwaiter().GetResult());
Seems to work correctly.
Is the 2nd snippet the correct pattern?
Is there a better pattern to achieve my goal?

Is there a better pattern to achieve my goal?
Yes. ContinueWhenAll is similar to ContinueWith; it's a low-level building block and I don't recommend using it. Use WhenAll instead:
Task task1 = RunTask1();
Task task2 = RunTask2();
await Task.WhenAll(task1, task2);
await RunTask3();
If you need the task instead of using await at this point in the code, a local async method will work fine:
Task task1 = RunTask1();
Task task2 = RunTask2();
Task task3_isDependentOnTasks1And2 = Run3When1And2AreDoneAsync();
async Task Run3When1And2AreDoneAsync()
{
await Task.WhenAll(task1, task2);
await RunTask3();
}

Related

How to run multiple methods in parallel in ASP.NET

I have 8 methods in an ASP.NET Console App, like Fun1(), Fun2(), Fun3() ... and so on. In my console application I have called all these methods sequentially. But now the requirement is I have do that using parallel programming concepts. I have read about task and threading concepts in Java but completely new to .NET parallel Programming.
Here is the flow of methods I needed in my console app,
As you can see the diagram, Task1 and Task2 should run in parallel, and Task3 will only occur after completion of the previous two.
The functions inside each task, for example Fun3 and Fun4 for the Task1, should run sequentially, the one after the other.
Can anyone please help me out?
One way to solve this is, by using WhenAll.
To take an example, I have created X number of methods with the name FuncX() like this:
async static Task<int> FuncX()
{
await Task.Delay(500);
var result = await Task.FromResult(1);
return result;
}
In this case, we have Func1, Func3, Func4, Func5, and Func6.
So we call methods and pass them to a list of Task.
var task1 = new List<Task<int>>();
task1.Add(Func3());
task1.Add(Func4());
var task2 = new List<Task<int>>();
task2.Add(Func1());
task2.Add(Func5());
task2.Add(Func6());
You have 2 options to get the result:
// option1
var eachFunctionIsDoneWithAwait1 = await Task.WhenAll(task1);
var eachFunctionIsDoneWithAwait2 = await Task.WhenAll(task2);
var sum1 = eachFunctionIsDoneWithAwait1.Sum() + eachFunctionIsDoneWithAwait2.Sum();
Console.WriteLine(sum1);
// option2
var task3 = new List<List<Task<int>>>();
task3.Add(task1);
task3.Add(task2);
var sum2 = 0;
task3.ForEach(async x =>
{
var r = await Task.WhenAll(x);
sum2 += r.Sum();
});
Console.WriteLine(sum2);
This is just example for inspiration, you can change it and do it the way you want.
Here is how you could create the tasks according to the diagram, using the Task.Run method:
Task task1 = Task.Run(() =>
{
Fun3();
Fun4();
});
Task task2 = Task.Run(() =>
{
Fun1();
Fun5();
Fun6();
});
Task task3 = Task.Run(async () =>
{
await Task.WhenAll(task1, task2);
Fun7();
Fun8();
});
The Task.Run invokes the delegate on the ThreadPool, not on a dedicated thread. If you have some reason to create a dedicated thread for each task, you could use the advanced Task.Factory.StartNew method with the TaskCreationOptions.LongRunning argument, as shown here.
It should be noted that the above implementation has not an optimal behavior in case of exceptions. In case the Fun3() fails immediately, the optimal behavior would be to stop the execution of the task2 as soon as possible. Instead this implementation will execute all three functions Fun1, Fun5 and Fun6 before propagating the error. You could fix this minor flaw by creating a CancellationTokenSource and invoking the Token.ThrowIfCancellationRequested after each function, but it's going to be messy.
Another issue is that in case both task1 and task2 fail, only the exception of the task1 is going to be propagated through the task3. Solving this issue is not trivial.
Update: Here is one way to solve the issue of partial exception propagation:
Task task3 = Task.WhenAll(task1, task2).ContinueWith(t =>
{
if (t.IsFaulted)
{
TaskCompletionSource tcs = new();
tcs.SetException(t.Exception.InnerExceptions);
return tcs.Task;
}
if (t.IsCanceled)
{
TaskCompletionSource tcs = new();
tcs.SetCanceled(new TaskCanceledException(t).CancellationToken);
return tcs.Task;
}
Debug.Assert(t.IsCompletedSuccessfully);
Fun7();
Fun8();
return Task.CompletedTask;
}, default, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default)
.Unwrap();
In case both task1 and task2 fail, the task3 will propagate the exceptions of both tasks.

Correct way to a-synchronize parallel tasks

Currently we have this code which works fine:
Result result1 = null;
Result result2 = null;
var task1 = Task.Factory.StartNew(()=>
{
var records = DB.Read("..");
//Do A lot
result1 = Process(records);
});
var task2 = Task.Factory.StartNew(()=>
{
var records = DB.Read(".....");
//Do A lot
result2 = Process(records);
});
Task.WaitAll(task1, task2);
var result = Combine(result1, result2);
Now we would like to use async counterparts of DB Functions and we are using this new pattern:
Result result1 = null;
Result result2 = null;
var task1 = await Task.Factory.StartNew( async ()=>
{
var records = await DB.ReadAsync("..");
//Do A lot
result1 = Process(records);
});
var task2 = await Task.Factory.StartNew(async ()=>
{
var records = await DB.ReadAsync(".....");
//Do A lot
result2 = Process(records);
});
Task.WaitAll(task1, task2);
var result = Combine(result1, result2);
After we switched to async we started observing abnormal behavior. So I wonder if this is the correct pattern to parallelize async calls ?
Task.Factory.StartNew is a pre-async API. You should be using Task.Run which was designed with async-await in mind:
var task1 = await Task.Run( async ()=>
{
var records = await DB.ReadAsync("..");
//Do A lot
result1 = Process(records);
});
The issue is that an async lambda returns a Task so Task.Factory.StartNew returns a Task<Task> (the outer one because Task.Factory.StartNew returns a Task and the inner one which is the result of the async lambda).
This means that when you wait on task1 and task2 you aren't really waiting for the entire operation, just the synchronous part of it.
You can fix that by using Task.Unwrap on the returned Task<Task>:
Task<Task> task1 = await Task.Factory.StartNew(async ()=>
{
var records = await DB.ReadAsync("..");
//Do A lot
result1 = Process(records);
});
Task actualTask1 = task1.Unwrap();
await actualTask1;
But Task.Run does that implicitly for you.
As a side note, you should realize that you don't need Task.Run to execute these operations concurrently. You can do that just by calling these methods and awaiting the results together with Task.When:
async Task MainAsync()
{
var task1 = FooAsync();
var task2 = BarAsync();
await Task.WhenAll(task1, task2);
var result = Combine(task1.Result, task2.Result);
}
async Task<Result> FooAsync()
{
var records = await DB.ReadAsync("..");
//Do A lot
return Process(records);
}
async Task<Result> BarAsync()
{
var records = await DB.ReadAsync(".....");
//Do A lot
return Process(records);
}
You only need Task.Run if you need to offload even the synchronous parts of these methods (the part before the first await) to the ThreadPool.
Well using .WaitAll is not an async programming because you're actually blocking current thread on waiting. Also you dont call .Unwrap and that's why you just wait only on creating of async lambda, not on async lambda itself.
Task.Run can unwrap async lambda for you. But there's a simpler and cleaner way.
var task1 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var task2 = DB.ReadAsync("..").ContinueWith(task => {
//Do A lot
return Process(task.Result);
}, TaskScheduler.Default);
var result = Combine(await task1, await task2);
In this way you will get result exactly when it's ready. So you don't need additional tasks and variables at all.
Please note that ContinueWith is a tricky function and it works on TaskScheduler.Current if it is not null and otherwise it works on TaskScheduler.Default which is thread pool scheduler. So it's safer to specify scheduler explicitly always when calling this function.
Also for claryfing I didn't included error checking because actually DB.ReadAsync can be completed with an error. But that's an easy thing and you can handle it for yourself.
Task.Factory.StartNew start a new Task of execution another independent execution unit. So the simplest way to deal with that may look like:
var task1 = Task.Factory.StartNew(()=> //NO AWAIT
{
var records = DB.Read("....."); //NO ASYNC
//Do A lot
result1 = Process(records);
});
... another task definition
Task.WaitAll(task1, task2);
Read and process sequentially in one task, as you have data dependency.

Using async in non-async method

Lets say I only want one method to run in async.
So I have an async method like below:
public async Task Load(){
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
And then I'm trying to call that async method in another method as a task, and would like for it to wait until that particular piece of async code is done. The problem is it's not. When it reaches the first await in Load() it just doesn't finish loading. The debugger goes blank and gives no other error.
Is an async method able to be called from a non async method, like this?
There is a reason I do not need this particular task to be async, but the Load() function I do.
public void GetSomethingElse(){
var task1 = Load().Wait();
}
How is this possible?
I tried even changing the Load() method to use var data = task1.Wait(), etc. instead of await, still no difference, no matter which way I try. If anyone can help it would be appreciated.
You probably have a deadlock on your hands. You're blocking a thread using Wait() on a task that needs that thread to complete because there's a SynchronizationContext being used in ASP.Net (also in GUI environments).
You should use ConfigureAwait(false) to tell the awaiter not to capture that context. It's enough to do that on the first await since the rest would have no SynchronizationContext to capture:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2;
var data3 = await task3;
//..process data.
}
However, it's recommended to always use ConfigureAwait unless you want to capture the SynchronizationContext so a better standard is this:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2.ConfigureAwait(false);
var data3 = await task3.ConfigureAwait(false);
//..process data.
}
In your case, where you want to continue after all the tasks completed you should use Task.WhenAll instead of awaiting each task separately:
public async Task Load()
{
await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false);
// process data.
}
Note: doing sync over async is usually discouraged as it has no benefits (you're blocking a thread throughout the entire operation) and may cause deadlocks (like this one).
I think you have a classic deadlock scenario. See this post for more details. When you have an await statement the current SynchronizationContext is stored before the await call and restored afterwards and the rest of the method posted to it. In GUI app there is only one thread associated with that context so the rest of the method is attemted to be executed on the GUI thread but it can't
because Wait() is a blocking call which blocks the GUI thread.
Try this instead:
public async Task Load(){
Task task1 = GetAsync(1).ConfigureAwait(false);
Task task2 = GetAsync(2).ConfigureAwait(false);
Task task3 = GetAsync(3).ConfigureAwait(false);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
If there are any awaits inside GetAsync you may have to add .ConfigureAwait(false) there as well.
You would change your Load function as following:
public async Task Load(){
await new TaskFactory().StartNew(() =>
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
});
}

What is Task.Current?

This question (Is there a way to tell which Tasks are currently running in Task Parallel Library?) says that you can access the currently running Task by the Task.Current property. But I can't find any such property in the documentation.
Why is this?
What would you want Task.Current to be, had it been there? Consider this:
async Task DoAsync()
{
var currentTask = Task.Current; // imagine Task.Current does exist
await Task.Delay(1000);
}
// ...
var task = Task.Run(() => DoAsync());
Would you want currentTask to be the task returned by Task.Run or by DoAsync?
Perhaps, a less contrived question would be this:
How to access the Task object from the task's own action?
You certainly cannot do it the following way, as it has a race condition:
Task task = null;
task = Task.Run(() => Console.WriteLine({ task.Id }));
You can however do it this way:
Task task = null;
task = new Task(() => Console.WriteLine({ task.Id }));
task.Run();
Is it what you're looking for? In my experience, I only once needed something like this:
Task sequencing and re-entracy

Task.WhenAll in PCL

I'm trying to run a few async tasks concurrently inside my Portable Class Library, but the WhenAll method doesn't appear to be supported.
My current workaround is to start each task and then await each of them:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var result1 = await task1;
var result1 = await task2;
var result1 = await task3;
Is there something I'm missing? Do I need to make do with the workaround?
Is there something I'm missing?
Yes: Microsoft.Bcl.Async.
Once you install that package, then you can use TaskEx.WhenAll as a substitute for Task.WhenAll:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var results = await TaskEx.WhenAll(task1, task2, task3);
P.S. Consider using the term parallel for (CPU-bound) parallel processing, and the term concurrent for doing more than one thing at a time. In this case, the (I/O-bound) tasks are concurrent but not parallel.

Categories

Resources