launch async methods when others finished, in undetermined order - c#

I have many Tasks, that are running asynchronously
Task<bool> task1 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcPolyline");
});
Task<bool> task2 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcPolyLoop");
});
Task<bool> task3 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcAxis2Placement2D");
});
Task<bool> task4 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcAxis2Placement3D");
});
Now, I would like to execute other tasks, as soon as some of them finish.
Let's say I have 3 tasks that need to be executed after that :
task5 needs to be executed when Task1 and Task2 finished.
task6 needs to be executed when Task3 and Task4 finished.
task7 needs to be executed when Task1 and Task6 finished.
How can I do that, cause if I use await Task.WhenAll(task1,task2) before calling task5, I also block execution of task6 and task7 ?

You could use to your advantage the fact that the Task.Run method accepts both synchronous and asynchronous delegates. Using an asynchronous delegate allows you to await previously started tasks:
var task1 = Task.Run(() => { /*...*/ });
var task2 = Task.Run(() => { /*...*/ });
var task3 = Task.Run(() => { /*...*/ });
var task4 = Task.Run(() => { /*...*/ });
var task5 = Task.Run(async () =>
{
await Task.WhenAll(task1, task2);
/*...*/
});
var task6 = Task.Run(async () =>
{
await Task.WhenAll(task3, task4);
/*...*/
});
var task7 = Task.Run(async () =>
{
await Task.WhenAll(task1, task6);
/*...*/
});

For a simple case, you can use Task.ContinueWith(). See Chaining tasks using continuation tasks on Microsoft Learn
For more complex case if you have lots of data to process, you can use DataFlow library to create a pipeline for asynchronous jobs. See How to: Implement a producer-consumer dataflow pattern

Related

Is there a way to wait for all tasks until a specific result is true, and then cancel the rest?

In my C# console application, I'm trying to run multiple tasks that do various data checks simultaneously. If one of the tasks returns true I should stop the other tasks since I have my actionable result. It's also very possible none of the functions return true
I have the code to run the tasks together (I think), I'm just having trouble getting to the finish line:
Task task1 = Task.Run(() => Task1(stoppingToken));
Task task2 = Task.Run(() => Task2(stoppingToken));
Task task3 = Task.Run(() => Task3(stoppingToken));
Task task4 = Task.Run(() => Task4(stoppingToken));
Task task5 = Task.Run(() => Task5(stoppingToken));
Task task6 = Task.Run(() => Task6(stoppingToken));
Task.WaitAll(task1, task2, task3, task4, task5, task6);
This is a little different than the answer in the linked question where the desired result is known (timeout value). I'm waiting for any of these tasks to possibly return true and then cancel the remaining tasks if they are still running
Task.WhenAny with cancellation of the non completed tasks and timeout
Here's a solution based on continuation tasks. The idea is to append continuation tasks to each of the original (provided) tasks, and check the result there. If it's a match, the completion source will be set with a result (if there's no match, the result won't be set at all).
Then, the code will wait for whatever happens first: either all the continuation tasks complete, or the task completion result will be set. Either way, we'll be ready to check the result of the task associated with task completion source (that's why we wait for the continuation tasks to complete, not the original tasks) and if it's set, it's pretty much an indication that we have a match (the additional check at the end is a little paranoid, but better safe than sorry I guess... :D)
public static async Task<bool> WhenAnyHasResult<T>(Predicate<T> isExpectedResult, params Task<T>[] tasks)
{
const TaskContinuationOptions continuationTaskFlags = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent;
// Prepare TaskCompletionSource to be set only when one of the provided tasks
// completes with expected result
var tcs = new TaskCompletionSource<T>();
// For every provided task, attach a continuation task that fires
// once the original task was completed
var taskContinuations = tasks.Select(task =>
{
return task.ContinueWith(x =>
{
var taskResult = x.Result;
if (isExpectedResult(taskResult))
{
tcs.SetResult(taskResult);
}
},
continuationTaskFlags);
});
// We either wait for all the continuation tasks to be completed
// (it's most likely an indication that none of the provided tasks completed with the expected result)
// or for the TCS task to complete (which means a failure)
await Task.WhenAny(Task.WhenAll(taskContinuations), tcs.Task);
// If the task from TCS has run to completion, it means the result has been set from
// the continuation task attached to one of the tasks provided in the arguments
var completionTask = tcs.Task;
if (completionTask.IsCompleted)
{
// We will check once more to make sure the result is set as expected
// and return this as our outcome
var tcsResult = completionTask.Result;
return isExpectedResult(tcsResult);
}
// TCS result was never set, which means we did not find a task matching the expected result.
tcs.SetCanceled();
return false;
}
Now, the usage will be as follows:
static async Task ExampleWithBooleans()
{
Console.WriteLine("Example with booleans");
var task1 = SampleTask(3000, true);
var task2 = SampleTask(5000, false);
var finalResult = await TaskUtils.WhenAnyHasResult(result => result == true, task1, task2);
// go ahead and cancel your cancellation token here
Console.WriteLine("Final result: " + finalResult);
Debug.Assert(finalResult == true);
Console.WriteLine();
}
What's nice about putting it into a generic method, is that it works with any type, not only booleans, as a result of the original task.
Assuming your tasks return bool you can do something like this:
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken stoppingToken = source.Token;
Task<bool> task1 = Task.Run(() => Task1(stoppingToken));
....
var tasks = new List<Task<bool>>
{
task1, task2, task3, ...
};
bool taskResult = false;
do
{
var finished = await Task.WhenAny(tasks);
taskResult = finished.Result;
tasks.Remove(finished);
} while (tasks.Any() && !taskResult);
source.Cancel();
You could use an asynchronous method that wraps a Task<bool> to another Task<bool>, and cancels a CancellationTokenSource if the result of the input task is true. In the example below this method is the IfTrueCancel, and it is implemented as local function. This way it captures the CancellationTokenSource, and so you don't have to pass it as argument on every call:
var cts = new CancellationTokenSource();
var stoppingToken = cts.Token;
var task1 = IfTrueCancel(Task.Run(() => Task1(stoppingToken)));
var task2 = IfTrueCancel(Task.Run(() => Task2(stoppingToken)));
var task3 = IfTrueCancel(Task.Run(() => Task3(stoppingToken)));
var task4 = IfTrueCancel(Task.Run(() => Task4(stoppingToken)));
var task5 = IfTrueCancel(Task.Run(() => Task5(stoppingToken)));
var task6 = IfTrueCancel(Task.Run(() => Task6(stoppingToken)));
Task.WaitAll(task1, task2, task3, task4, task5, task6);
async Task<bool> IfTrueCancel(Task<bool> task)
{
bool result = await task.ConfigureAwait(false);
if (result) cts.Cancel();
return result;
}
Another, quite different, solution to this problem could be to use PLINQ instead of explicitly created Tasks. PLINQ requires an IEnumerable of something in order to do parallel work on it, and in your case this something is the Task1, Task2 etc functions that you want to invoke. You could put them in an array of Func<CancellationToken, bool>, and solve the problem this way:
var functions = new Func<CancellationToken, bool>[]
{
Task1, Task2, Task3, Task4, Task5, Task6
};
bool success = functions
.AsParallel()
.WithDegreeOfParallelism(4)
.Select(function =>
{
try
{
bool result = function(stoppingToken);
if (result) cts.Cancel();
return result;
}
catch (OperationCanceledException)
{
return false;
}
})
.Any(result => result);
The advantage of this approach is that you can configure the degree of parallelism, and you don't have to rely on the ThreadPool availability for limiting the concurrency of the whole operation. The disadvantage is that all functions should have the same signature. You could overcome this disadvantage by declaring the functions as lambda expressions like this:
var functions = new Func<CancellationToken, bool>[]
{
ct => Task1(arg1, ct),
ct => Task2(arg1, arg2, ct),
ct => Task3(ct),
ct => Task4(arg1, arg2, arg3, ct),
ct => Task5(arg1, ct),
ct => Task6(ct)
};

Wait for a task in multiple tasks at the same time

Imagine an initialization task and two workers:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
await Init;
Console.WriteLine("B finished waiting");
await Task.Delay(10000000);
});
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
await Task.Delay(10000000);
});
After 1 second, TaskB and TaskC are printing to the console and continuing as expected.
However, when the task which finishes first uses no asynchronous methods after awaiting Init, the other task await Init call does not finish:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
await Init;
Console.WriteLine("B finished waiting");
await Task.Delay(5000);
});
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
while (true) { }
});
The latter example prints only "C finished waiting" to the console. Why is that so?
Behind the scenes, async/await builds a state machine based on task continuations. You can mimic it using ContinueWith and a bit of ingenuity (the details are beyond the scope of this answer).
What you need to know is that it uses continuations, and that continuations - by default - happen syncrhonously, one at a time.
In this case, both B and C are creating continuations on Init. These continuations will run after the other. Thus, when C continues first and goes into the infinite loop, it prevents B to continue.
The solution? Well, other than avoiding infinite loops, you could use a task with asynchronous continuations. A simple way to do it on top of Task.Run is the following:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
}).ContinueWith(_ => { }, TaskContinuationOptions.RunContinuationsAsynchronously);
Addendum
As per OP comment, the infinite loop represent a blocking call. We can solve that situation by a different approach:
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
await Task.Run(() => {while (true) { }});
});
This way, the blocking call is not int the continuation of Init (instead, it would be in a continuation of a continuation), and thus it won't prevent other continuations of Init to run.

How to ensure callbacks of parent tasks has been completed in child task

internal int SomeFunction()
{
Task<AddResult> task1 = new Task<AddResult>(() => AddFunction());
task1.Start();
Task<FuncResult> task2 = task1.ContinueWith(task => func1(task1.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task3 = task2.ContinueWith(task => func2(task2.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task4 = task3.ContinueWith(task => func3(task2.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
return 200;
}
void callback(byte response)
{
}
in above func1 and func2 functions will send some data to a device and response will be received in callback function.
func3 will save data into database but before that i need to ensure that all the callbacks are completed. how can i achieve this.
Make use of Task.WhenAll() and store your individual tasks in a collection.
List<Task<t>> _tasks = new List<Task<t>>();
// Now add your tasks...
_tasks.Add(Task<DeviceInfo>(() => AddNodeToNetwork((Modes)mode)));
// Next task, etc.
Finally await for all tasks. This will run the awaiter on the calling thread.
await Task.WhenAll(_tasks);
This will run the awaiter from the a thread pool thread.
await Task.WhenAll(_tasks).ConfigureAwait(false);
I think the trick is to refer to the parameter called 'task' in your lambda expressions. This is the task you're following on from, so if you refer to 'task' rather than 'task1', 'task2', 'task3' etc, then by asking for task.Result, you guarantee that the previous task has been executed.
The only other thing is that you will need to wait for task4 (the final one) to complete, which you could do with a call to Task.WaitAll(new [] { task4 })
internal int SomeFunction()
{
Task<AddNodeResult> task1 = new Task<DeviceInfo>(() => AddNodeToNetwork());
task1.Start();
Task<ZWNode> task2 = task1.ContinueWith(task => GetCommandClassesVersions(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task3 = task2.ContinueWith(task => GetManufacturerSpecific(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task4 = task3.ContinueWith(task => PersistAddedNode(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
return 200;
}
Apologies if this doesn't compile -- just typing in a text editor here!

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