Execute list of async tasks - c#

I have a list of async functions I want to execute in order. When I run the following code I get the output:
Task 1 before
Task 2 before
Finished tasks
Why are my async functions not being awaited correctly?
[Test]
public async Task AsyncTaskList()
{
var data = "I'm data";
var tasks = new List<Func<object, Task>>() {Task1, Task2};
tasks.ForEach(async task =>
{
await task(data);
});
Debug.WriteLine("Finished tasks");
}
private static async Task Task1(object data)
{
Debug.WriteLine("Task 1 before");
await Task.Delay(1000);
Debug.WriteLine("Task 1 after");
}
private static async Task Task2(object data)
{
Debug.WriteLine("Task 2 before");
await Task.Delay(1000);
Debug.WriteLine("Task 2 after");
}

Because the await inside your ForEach delegate actually completes after the method exits. Change it to an actual foreach loop and awaiting will work as expected.
ForEach has no specific handling for Func<Task> (few delegate-accepting methods in the Base Class Library do, and you should note that they will almost invariably return a Task themselves). ForEach will only run the synchronous portion of your lambda - and that is the portion preceding the first await of a Task which does not complete synchronously (which is Task.Delay in your case). This is why you're seeing the "before" messages popping up at the expected time. As soon as your delegate hits await Task.Delay, the the rest of your lambda is scheduled to run sometime in the future and ForEach moves on to the next item in the list. The scheduled task continuations will then run unobserved and complete later.

Related

It's possible to give priority to a certain Task when executing more than one?

I'm executing an async function (ExampleFunction) that blocks the thread for the following calls if the previous one hasn't finished.
I'm printing messages in the console to check the order, but the order is not the expected.
So, this is the code:
private async Task TestTest()
{
int idtmp = id;
var task1 = Test(idtmp);
id++;
int idtmp2 = id;
var task2 = Test(idtmp2);
id++;
await Task.WhenAll(task1, task2).ConfigureAwait(false);
Console.ReadLine();
}
private async Task Test(int id)
{
Console.WriteLine($"Before calling {id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
Task<int> task = ExampleFunction();
Console.WriteLine($"After calling {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
await task.ConfigureAwait(false);
Console.WriteLine($"Function finished {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}
This is the output:
Before calling 1 17:23:42.5953
After calling 1 17:23:42.6356
Before calling 2 17:23:42.6371
After calling 2 17:24:07.0415
Function finished 1 17:24:07.0453
Function Finished 2 17:31.2036
The expected output should be:
Before calling 1
After calling 1
Before calling 2
Function finished 1
After calling 2
Function Finished 2
But it seems that when the first Task finishes, the second one continues until it hits the await and that's why it shows the After calling 2 message first.
Is there a way to specify that the Task1 has priority when the first call finishes?
EDIT:
The ExampleFunction has two behaviors in the server side:
The second call will block the thread if there is another call in progress
The second call won't block the thread, it will be queued in the background.
So, for the second behavior the output should be this:
Before calling 1
After calling 1
Before calling 2
After calling 2
Function finished 1
Function Finished 2
But that's the output that I'm receiving with the first behavior.
I need to check the correct behavior in each configuration.
That's why the current method doesn't work for me.
If I've understood you correctly, the key thing here is that you want to start the second task at the same time as the first to let service calls begin, but you can't finish the second task until you have the results of the first.
If so, your second task needs to look like this:
public async Task Task2(int id, Task<Task1State> task1)
{
/* Start Service calls */
await Task.Delay(TimeSpan.FromSeconds(1.0));
var state = await task1;
/* Complete `Task2` with the `state` from `Task1` */
await Task.Delay(TimeSpan.FromSeconds(1.0));
}
The await Task.Delay calls are there to simulate work.
Now it's easy to harness up a test:
async Task Main()
{
var id = 0;
var task1_id = ++id;
var task2_id = ++id;
Console.WriteLine($"Before calling {task1_id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task1 = Task1(task1_id);
Console.WriteLine($"After calling {task1_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
Console.WriteLine($"Before calling {task2_id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task2 = Task2(task2_id, task1);
Console.WriteLine($"After calling {task2_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
await task2;
Console.WriteLine($"Function finished {task2_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}
public class Task1State { }
public async Task<Task1State> Task1(int id)
{
await Task.Delay(TimeSpan.FromSeconds(2.0));
Console.WriteLine($"Function finished {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
return new Task1State();
}
When I run that I get the following expected results:
Before calling 1 14:16:13.0724
After calling 1 14: 16:13.0736
Before calling 2 14:16:13.0736
After calling 2 14: 16:13.0743
Function finished 1 14: 16:15.0889
Function finished 2 14: 16:16.1039
The combined wait time is 4 seconds, but because two of those seconds happen in parallel then the total time is 3 seconds as seen in the output.
Async methods will run synchronously until they hit the first await of an incomplete task. If you think about how much of your code might run synchronously at each await it would be equivalent to;
int idtmp = id;
Console.WriteLine($"Before calling {idtmp} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task1 = ExampleFunction();
Console.WriteLine($"After calling {idtmp} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
if (task1.IsCompleted){
Console.WriteLine($"Function finished {idtmp} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}else{
// async magic
}
id++;
int idtmp2 = id;
Console.WriteLine($"Before calling {idtmp2} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task2 = ExampleFunction();
Console.WriteLine($"After calling {idtmp2} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
if (task2.IsCompleted){
Console.WriteLine($"Function finished {idtmp2} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}else{
// async magic
}
Then you also need to consider what ExampleFunction will do before calling it's first await. If it's a CPU heavy task, it might take a long time to return. Does the second call block the current thread on a lock until the first call returns? If there is some lock synchronisation between tasks, does it matter which thread / task resumes first?
While it is possible for the first async method to complete in the order you are hoping for. Is it likely?
I hope this modified example makes things a bit more clear.
Notes:
The tasks are started before calling WhenAll
A task runs synchronously until the first await so it will not yield control before that (in the case of a fake task with no awaits it will never yield control)
All work of is done by the same thread until the awaits 'come back'.
var task1 = Test("A");
var task2 = Test("B");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Sleeping");
await Task.Delay(TimeSpan.FromMilliseconds(300));
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Before WhenAll");
await Task.WhenAll(task1,task2).ConfigureAwait(false);
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: After WhenAll");
Console.ReadLine();
async Task Test(string s)
{
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: Started {s}");
Task task = Task.Delay(TimeSpan.FromMilliseconds(100));
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: After starting work {s}");
await task.ConfigureAwait(false);
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: Finished {s}");
}
This prints
1: Started A
1: After starting work A
1: Started B
1: After starting work B
1: Sleeping
5: Finished B
7: Finished A
5: Before WhenAll
5: After WhenAll
You want to have two Tasks that are Mutually Exclusive.
In the async programming world we have a synchronization primitive called the Mutex.
You want the server side to have an Async Mutex.
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
public class Server
{
private readonly AsyncLock _asyncMutext = new();
public async Task Foo()
{
using var _ = await _asyncMutext.LockAsync();
Console.WriteLine("Starting Foo");
await Task.Delay(1000);
Console.WriteLine("Finishing Foo");
}
public async Task Bar()
{
using var _ = await _asyncMutext.LockAsync();
Console.WriteLine("Starting Bar");
await Task.Delay(1000);
Console.WriteLine("Finishing Bar");
}
}
Just scope your Mutex appropriately for your use case.

How to synchronize the recurrent execution of three tasks that depend on each other?

I would like to ask expert developers in C#. I have three recurrent tasks that my program needs to do. Task 2 depends on task 1 and task 3 depends on task 2, but task 1 doesn't need to wait for the other two tasks to finish in order to start again (the program is continuously running). Since each task takes some time, I would like to run each task in one thread or a C# Task. Once task 1 finishes task 2 starts and task 1 starts again ... etc.
I'm not sure what is the best way to implement this. I hope someone can guide me on this.
One way to achieve this is using something called the the Task Parallel Library. This provides a set of classes that allow you to arrange your tasks into "blocks". You create a method that does A, B and C sequentially, then TPL will take care of running multiple invocations of that method simultaneously. Here's a small example:
async Task Main()
{
var actionBlock = new ActionBlock<int>(DoTasksAsync, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 2 // This is the number of simultaneous executions of DoTasksAsync that will be run
};
await actionBlock.SendAsync(1);
await actionBlock.SendAsync(2);
actionBlock.Complete();
await actionBlock.Completion;
}
async Task DoTasksAsync(int input)
{
await DoTaskAAsync();
await DoTaskBAsync();
await DoTaskCAsync();
}
I would probably use some kind of queue pattern.
I am not sure what the requirements for if task 1 is threadsafe or not, so I will keep it simple:
Task 1 is always executing. As soon as it finished, it posts a message on some queue and starts over.
Task 2 is listening to the queue. Whenever a message is available, it starts working on it.
Whenever task 2 finishes working, it calls task 3, so that it can do it's work.
As one of the comments mentioned, you should probably be able to use async/await successfully in your code. Especially between task 2 and 3. Note that task 1 can be run in parallel to task 2 and 3, since it is not dependent on any of the other task.
You could use the ParallelLoop method below. This method starts an asynchronous workflow, where the three tasks are invoked in parallel to each other, but sequentially to themselves. So you don't need to add synchronization inside each task, unless some task produces global side-effects that are visible from some other task.
The tasks are invoked on the ThreadPool, with the Task.Run method.
/// <summary>
/// Invokes three actions repeatedly in parallel on the ThreadPool, with the
/// action2 depending on the action1, and the action3 depending on the action2.
/// Each action is invoked sequentially to itself.
/// </summary>
public static async Task ParallelLoop<TResult1, TResult2>(
Func<TResult1> action1,
Func<TResult1, TResult2> action2,
Action<TResult2> action3,
CancellationToken cancellationToken = default)
{
// Arguments validation omitted
var task1 = Task.FromResult<TResult1>(default);
var task2 = Task.FromResult<TResult2>(default);
var task3 = Task.CompletedTask;
try
{
int counter = 0;
while (true)
{
counter++;
var result1 = await task1.ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
task1 = Task.Run(action1); // Restart the task1
if (counter <= 1) continue; // In the first loop result1 is undefined
var result2 = await task2.ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
task2 = Task.Run(() => action2(result1)); // Restart the task2
if (counter <= 2) continue; // In the second loop result2 is undefined
await task3.ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
task3 = Task.Run(() => action3(result2)); // Restart the task3
}
}
finally
{
// Prevent fire-and-forget
Task allTasks = Task.WhenAll(task1, task2, task3);
try { await allTasks.ConfigureAwait(false); } catch { allTasks.Wait(); }
// Propagate all errors in an AggregateException
}
}
There is an obvious pattern in the implementation, that makes it trivial to add overloads having more than three actions. Each added action will require its own generic type parameter (TResult3, TResult4 etc).
Usage example:
var cts = new CancellationTokenSource();
Task loopTask = ParallelLoop(() =>
{
// First task
Thread.Sleep(1000); // Simulates synchronous work
return "OK"; // The result that is passed to the second task
}, result =>
{
// Second task
Thread.Sleep(1000); // Simulates synchronous work
return result + "!"; // The result that is passed to the third task
}, result =>
{
// Third task
Thread.Sleep(1000); // Simulates synchronous work
}, cts.Token);
In case any of the tasks fails, the whole loop will stop (with the loopTask.Exception containing the error). Since the tasks depend on each other, recovering from a single failed task is not possible¹. What you could do is to execute the whole loop through a Polly Retry policy, to make sure that the loop will be reincarnated in case of failure. If you are unfamiliar with the Polly library, you could use the simple and featureless RetryUntilCanceled method below:
public static async Task RetryUntilCanceled(Func<Task> action,
CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try { await action().ConfigureAwait(false); }
catch { if (cancellationToken.IsCancellationRequested) throw; }
}
}
Usage:
Task loopTask = RetryUntilCanceled(() => ParallelLoop(() =>
{
//...
}, cts.Token), cts.Token);
Before exiting the process you are advised to Cancel() the CancellationTokenSource and Wait() (or await) the loopTask, in order for the loop to terminate gracefully. Otherwise some tasks may be aborted in the middle of their work.
¹ It is actually possible, and probably preferable, to execute each individual task through a Polly Retry policy. The parallel loop will be suspended until the failed task is retried successfully.

Task.WhenAll(IEnumerable): Tasks are started twice?

I just stumbled upon one of the overloads of Task.WhenAll, the one that takes an IEnumerable as parameter
public static Task WhenAll(IEnumerable<Task<TResult>> tasks)
I thought I'd try this function with the following short program.
In a Test class:
// contains the task numbers that has been run
private HashSet<int> completedTasks = new HashSet<int>();
// async function. waits a while and marks that it has been run:
async Task<int> Calculate(int taskNr)
{
string msg = completedTasks.Contains(taskNr) ?
"This task has been run before" :
"This is the first time this task runs";
Console.WriteLine($"Start task {i} {msg}");
await Task.Delay(TimeSpan.FromMilliseconds(100));
Console.WriteLine($"Finished task {taskNr}");
// mark that this task has been run:
completedTasks.Add(taskNr);
return i;
}
// async test function that uses Task.WhenAll(IEnumerable)
public async Task TestAsync()
{
Console.Write("Create the task enumerators... ");
IEnumerable<Task<int>> tasks = Enumerable.Range(1, 3)
.Select(i => Calculate(i));
Console.WriteLine("Done!");
Console.WriteLine("Start Tasks and await");
await Task.WhenAll(tasks);
Console.WriteLine("Finished waiting. Results:");
foreach (var task in tasks)
{
Console.WriteLine(task.Result);
}
}
Finally the main program:
static void Main(string[] args)
{
var testClass = new TestClass();
Task t = Task.Run(() => testClass.TestAsync());
t.Wait();
}
The output is as follows:
Create the task enumerators... Done!
Start Tasks and wait
Start task 1 This is the first time this task runs
Start task 2 This is the first time this task runs
Start task 3 This is the first time this task runs
Finished task 2
Finished task 3
Finished task 1
Finished waiting. Results:
Start task 1 This task has been run before
Finished task 1
1
Start task 2 This task has been run before
Finished task 2
2
Start task 3 This task has been run before
Finished task 3
3
Apparently each task is run twice! What am I doing wrong?
Even stranger: if I enumerate over the sequence of Tasks using ToList() before the Task.Whenall, the function works as expected!
Your problem is deferred execution. Change this line
IEnumerable<Task<int>> tasks = Enumerable.Range(1, 3)
.Select(i => Calculate(i));
to
var tasks = Enumerable.Range(1, 3)
.Select(i => Calculate(i)).ToList();
Select() does not execute the "query" immediatly, but returns an enumerator. Only if you use this enumerator to iterate through the tasks, the inner lambda is called for the sequence 1...3.
In your version, every time you iterate through tasks, Calculate(i) is called again and new tasks are created.
With .ToList() the enumerator is executed once and the resulting sequence of Task<int> is stored in a List<Task<int>> (and not generated again when that list is enumerated a second time).
When you call Task.WhenAll(tasks) this method iterates through tasks and thereby starts each task. When you later iterate again (with your foreach loop to output the result), the query is executed again and thereby new tasks are started.

Why is my method not running asynchronously?

I'm running a test that should be calling a method multiple times without waiting for its result. Here's my code:
private async Task HandleJob(string params) {
// do stuff that takes a minute
Thread.Sleep(10000);
return;
}
[TestMethod]
public async Task StartTest() {
HandleJob("one");
HandleJob("two");
}
When I set a break at HandleJob("two"), it only gets hit after one has been completed. How do I make them run asynchronously, so no waiting is done?
You haven't used await in the method at all. This means that the method will execute the lines synchronously. To actually yield back to the caller and let the remaining code run asynchronously, you need to use await, which will continue the method as a continuation on the targeted Task (as well as unboxing any exceptions and return values)
There are a couple of helper methods in Task to assist with this - my favorite is Task.Yield(), which will immediately return, thus an await Task.Yield() will spit out into a new thread and return all at once.
[TestMethod]
public async Task StartTest() {
await Task.Yield(); // await yields control, and Yield will ensure the rest of the method starts immediately
HandleJob("one");
HandleJob("two");
}
If you want the individual subtasks to execute all at once, conglomerate them using Task.WhenAll and Task.Run
[TestMethod]
public async Task StartTest() {
await Task.WhenAll(
Task.Run(() => HandleJob("one")),
Task.Run(() => HandleJob("two")));
}
Your asynchronous method HandleJob is not really async as Thread.Sleep blocks the current thread. Try using await Task.Delay instead. Note that params is a keyword and your original code won't compile. I've changed it to parameters.
private async Task HandleJob(string parameters) {
// do stuff that takes a minute
await Task.Delay(10000);
return;
}
In your start method you can return a single Task that gets completed when both methods are finished using Task.WhenAll as pointed out in one of the other answers. If you return the result rather than await it, then the caller of StartTest can determine whether he waits for the Task to complete or if he ignores it and it becomes a fire and forget situation. Because you are not using await StartTest will not need to be marked as async anymore.
[TestMethod]
public Task StartTest() {
return Task.WhenAll(HandleJob("one"), HandleJob("two"));
}
you need to await both the tasks:
private void HandleJob( params string[] value )
{
return;
}
[TestMethod]
public async Task StartTest()
{
var task1 = Task.Run( () => HandleJob( "one" ) );
var task2 = Task.Run( () => HandleJob( "two" ) );
await Task.WhenAll( task1 , task2 );
}

Prevent async method from returning until all tasks are completed

So I'm still trying to understand the async/await pattern, but I'm also trying to achieve the following behavior:
A method A calls method B which runs a number of processes. Some of those processes can be run on separate threads while other things are being processed so that their return values will be available closer to when they are needed. Method B needs to not return control to the caller until all of these processes are completed.
Here is the test code that I am working with:
static void Main(string[] args)
{
CallProc();
Console.WriteLine("Program finished");
Console.ReadKey();
}
public static async Task CallProc()
{
var two = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(5000, "two"));
var one = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(500, "one"));
var three = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(1500, "three"));
// some process happens here
var oneMessage = await one; // waits until one finishes and then snags it's value
Console.WriteLine("Got message {0}", oneMessage);
// some more stuff happens here
var twoMessage = await two; // waits until two is finished and then snags it's value
Console.WriteLine(twoMessage);
// TODO: need to make sure that everything is completed before returning control to caller
}
public static string SomeSynchronousProcessIDontOwn(int delayTime, string message, bool delay = true)
{
Console.WriteLine("Starting \"{0}\"", message);
if(delay) Thread.Sleep(delayTime);
return string.Format("Finished \"{0}\"", message);
}
Right now, what is happening is that everything words as I expected except that the method is returning before everything is finished, so the output shows "Program finished" while "two" is still running.
How do I write this so that CallProc() can execute those tasks asynchronously but delay returning until everything has been completed. In other words, CallProc() needs to run some tasks asynchronously, but CallProc() itself needs to be called synchronously.
The idea of an asynchronous method, which is what you've written is that it will return control (approximately) immediately and the task that it returns will be marked as completed when the operation that it conceptually represents finishes.
This means that your program should either be looking at the resulting task to see when it finishes, or that you don't want an asynchronous method in the first place, and you should re-write CallProc synchronously rather than asynchronously.
To make CallProc synchronous simply remove async (and adjust the return type accordingly), and wait on each task instead of using await.
If CallProc really should be asynchronous then the caller should be adding a continuation (or using await) to perform an action when the task is completed, rather than when the method returns.
Instead of awaiting each task individually why not just await all of them using WhenAll
public static async Task CallProc()
{
var two = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(5000, "two"));
var one = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(500, "one"));
var three = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(1500, "three"));
// run synchronous tasks
await Task.WhenAll(one, two, three);
}
If you would prefer to have CallProc block (i.e. not return until all tasks have finished) then remove the async declaration and use Task.WaitAll instead.
public static void CallProc()
{
// start tasks
Task.WaitAll(one, two, three);
}
One way to do this is to simply call Wait() on the result of CallProc. This will essentially wait for the task returned by CallProc to finish before it continues. Calling Wait in a GUI app can cause a deadlock but for a console app it is fine.
static void Main(string[] args)
{
CallProc().Wait();
Console.WriteLine("Program finished");
Console.ReadKey();
}
That will ensure that "Program finished" is printed after task two is finished.

Categories