.Net parallel WaitAll() - c#

I have a situation in my code where i'm starting unknown number of tasks and would like to use Task.WaitAll().
something like this:
if (condition)
{
var task1 = Task.Factory.StartNew (call the web service1...);
}
if (condition)
{
var task2 = Task.Factory.StartNew (call the web service2...);
}
if (condition)
{
var task3 = Task.Factory.StartNew (call the web service3...);
}
Task.WaitAll(task1, task2, task3);
The problem is that i can't say
Task.WaitAll(task1, task2 , task3)
because i don't know which one of them will actually start.
Any idea for a solution?

You can use the list of Tasks and add your tasks into list dynamically:
var tasks = new List<Task>();
if (condition)
{
var task = Task.Factory.StartNew (call the web service1...);
tasks.Add(task);
}
if (condition)
{
var task2 = Task.Factory.StartNew (call the web service2...);
tasks.Add(task2);
}
if (condition) {
var task3 = Task.Factory.StartNew (call the web service3...);
tasks.Add(task3);
}
Task.WaitAll(tasks.ToArray());

Create a list of actually started tasks and do Task.WaitAll(taskList.ToArray())
if(condition)
{
var task1 = Task.Factory.StartNew (call the web service1...);
taskList.Add(task1);
}
// etc...

Generally, it is possible to save the list of tasks to the List (tasks) and use the
following code:
Task.WaitAll(tasks.ToArray());

See How to: Wait on One or More Tasks to Complete. You can wait on an array of Tasks.

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.

Firebase Unity - getDownloadURLAsync for multiple images

I'm trying to retrieve the download urls for 2 images from Firebase, in order to pass on to another method as strings to download them.
For a single image, I'm using this code :
img1_ref.GetDownloadUrlAsync().ContinueWith((Task<Uri> task) => {
if (!task.IsFaulted && !task.IsCanceled) {
UnityMainThreadDispatcher.Instance().Enqueue(() => UIcontroller.displayImage(task.Result.ToString()));
}
});
Which works perfectly - but I can't figure out how to fetch two urls, make sure I've got them all, then pass them onto the download method...
I'm struggling to get my head around aSync...
Any help grateful received!
If you want to use Continuations, it's relatively straightforward.
First I'd suggest changing your original code to this:
img1_ref.GetDownloadUrlAsync().ContinueWithOnMainThread((Task<Uri> task) => {
if (!task.IsFaulted && !task.IsCanceled) {
UIcontroller.displayImage(task.Result.ToString()));
}
});
This uses a Firebase extension to task to move your logic onto the main thread in a more concise manner.
Next, you can use Task.WhenAll to create a task that waits for multiple tasks to complete:
var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();
Task.WhenAll(task1, task2).ContinueWithOnMainThread((Task<Uri>[] tasks) => {
if (!task.IsFaulted && !task.IsCanceled) {
// work with tasks here. You can also use IEnumerable<Task<Uri>>
}
});
Of course, now we can have a little fun. I'd recommend reading this article on threading in Firebase for a little more background.
You can use #Frank van Puffelen's answer and do something like this:
async void DownloadAsync() {
var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();
await Task.WhenAll(task1, task2);
// you're on the calling thread here, which is usually the main thread
UIcontroller.displayImage(task1.Result.ToString());
}
Or you can move this all to a coroutine to get a little bit more Unity-aware memory safety:
IEnumerator DownloadCoroutine() {
var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();
yield return new WaitUntil(()=>Task.WhenAll(task1, task2).IsComplete);
// coroutines always run on the main thread, and only if this hasn't been destroyed
UIcontroller.displayImage(task1.Result.ToString());
}
Based on Running multiple async tasks and waiting for them all to complete that'd be:
var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();
await Task.WhenAll(task1, task2);
...

Execute a Method after completion of any one task in c#

i have two tasks running asynchronously.
var task1=Task.Run(async()=>{method1();});
var task2=Task.Run(async()=>{method1();});
If any one of the task completed,i want to run an another method(eg:method2()).And after completion of second task, i want to run the same method again(i.e.,method2()) .How to do this?
Assuming your methods are awaitable, Maybe you want something like this
await Task.WhenAny(method1,method1); // wait for something to finish
await method2(); // await for method 2
await Task.WhenAll(method1,method1); // run it all again
// or endlessly
while(!theEndofTheUniverse)
{
await Task.WhenAny(method1,method1);
await method2();
} // rinse and repeate
If any one of the task completed,i want to run an another method(eg:method2()).And after completion of second task, i want to run the same method again(i.e.,method2())
Sounds like you have two parallel paths of execution, each composed of two method calls in series. So....
var task1 = Task.Run( async () => { await method1(); await method2(); });
var task2 = Task.Run( async () => { await method1(); await method2(); });
await Task.WhenAll( new Task[] { task1, task2 } );
If I understand you correctly you have a bunch of tasks and if one of them is finshed you want to run another task?
You can use Task.WhenAny(). It accepts an array of tasks and you can await until the first one is finished
var tasks = new[] { Method1(), Method2(), Method3() };
await Task.WhenAny(tasks);
await Method4();
MSDN: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenany?view=netcore-3.1

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.

Call long running method and continue with other tasks

public class PerformMainTask()
{
Task1();
Task2();
PerformLongTask();
Task3();
Task4();
}
What I would like to achieve here is to PerformLongTask() onto another thread, and to continue with Task3 & Task4 even when PerformLongTask() is still running.
How should my PerformLongTask() be like in a C# 5.0 way?
Do I need to use async/await?
The simplest solution I know of is:
Task1();
Task2();
var task3 = Task.Run(() => PerformLongTask());
Task4();
Task5();
task3.Wait(); //if task3 has not started yet it will be inlined here
Simple and efficient. If you need to propagate errors you should probably use Parallel.Invoke:
Parallel.Invoke(
() => { PerformLongTask(); },
() => { Task4(); Task5(); }
);
Assuming you are using C# 5 and all your Task() methods truly return a Task (or anything awaitable), your code should look like that:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = PerformLongTask();
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
However, if your long task does not run parallelly on its own, you can force it to do so with Task.Run:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = Task.Run(PerformLongTask);
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
If none are your tasks are really tasks, just strip all the await except the last one, and you will be good to go.
public class PerformMainTask()
{
Task1();
Task2();
Task.WaitAll(Task.Factory.StartNew(() => PerformLongTask()), Task.Factory.StartNew(() => { Task3(); Task4(); }));
}

Categories

Resources