Await vs await Task.Delay trying to learn - c#

If I have code that says:
public async Task Test1()
{
Task task1 = MakeEggAsync();
Task task2 = MakeBaconAsync();
await Task.WhenAll(task1, task2);
}
async Task MakeBaconAsync()
{
while (CookIsBusy)
{
//
await Task.Delay(100);
}
}
async Task MakeEggAsync()
{
await makeEgg2Async();
}
async Task makeEgg2Async()
{
while (CookIsBusy)
{
//
await Task.Delay(100);
}
}
...will the computer return to the main Test1() after it get to this line?
async Task MakeEggAsync()
{
await makeEgg2Async();
}
...or will it return only after it get to a delay? I know with threading it only returns after you get to a wait.
Sorry I am new to this and I am trying to learn.
Does the computer returns after it gets to an await or after it get to a Task.Delay? This is what I am really asking.

Every async method begins executing synchronously.
Also, objects are awaited, not methods. In other words, this code:
async Task MakeEggAsync()
{
await makeEgg2Async();
}
is roughly the same as this code:
async Task MakeEggAsync()
{
var task = makeEgg2Async();
await task;
}
So the computer returns to Test1 after the Task.Delay is invoked.

Related

Task.Delay not delaying async method

I'm trying to create multiple tasks, run them in parallel, and wait for them all to finish.
public class SimulationManager
{
public List<Task> Simulations = new List<Task>();
public void AddSimulation(SimulationParameters parameters)
{
Simulations.Add(new Task(async () => await new Simulation().Simulate()));
}
public async Task StartSimulations()
{
Simulations.ForEach(s => s.Start());
await Task.WhenAll(Simulations);
Console.WriteLine("All tasks finished");
}
}
The task itself delays the execution by one second and then prints out a message.
public class Simulation
{
public async Task Simulate()
{
Console.WriteLine("Simulating");
await Task.Delay(1000);
}
}
I would expect the output to be:
Simulating
All tasks finished
Instead, I get:
All tasks finished
Simulating
If I replace await Task.Delay(1000) with Thread.Sleep(1000) it works as expected.
Why is the task being marked as completed without actually being completed?
If I read the status of the task before and after Task.WhenAll, it is awaiting correctly. The problem is then that Task.Delay is not delaying the execution even though the method is async.
Simulations.ForEach(s => s.Start());
Console.WriteLine(Simulations.First().Status); // prints "WaitingToRun"
await Task.WhenAll(Simulations);
Console.WriteLine(Simulations.First().Status); // prints "RanToCompletion"
Console.WriteLine("All tasks finished");
Remove redundant Task wrapper - this is what causing the issues.
Store simulations as a function returning Task and start simulation explicitly by invoking it.
public class SimulationManager
{
public List<Func<Task>> Simulations = new List<Func<Task>>();
public void AddSimulation(SimulationParameters parameters)
{
Simulations.Add(() => new Simulation().Simulate());
}
public async Task StartSimulations()
{
var tasks = Simulations.Select(simulate => simulate()).ToArray();
await Task.WhenAll(tasks);
Console.WriteLine("All tasks finished");
}
}
You are waiting on the wrong thing. Here is a fixed version
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskSimulateEarlyReturn
{
public class Simulation
{
public async Task Simulate()
{
Console.WriteLine("Simulating");
await Task.Delay(1000);
Console.WriteLine("Finished Simulating");
}
}
public class SimulationManager
{
public List<Task<Task>> Simulations = new List<Task<Task>>();
public void AddSimulation()
{
Simulations.Add(new Task<Task>(async () => await new Simulation().Simulate()));
}
public async Task StartSimulations()
{
for(int i=0;i<4;i++)
{
AddSimulation();
}
Simulations.ForEach(s => s.Start());
await Task.WhenAll(Simulations.Select(x=>x.Unwrap()).ToArray());
Console.WriteLine("All tasks finished");
}
}
class Program
{
static void Main(string[] args)
{
var man = new SimulationManager();
man.StartSimulations().Wait();
Thread.Sleep(1000);
}
}
}
The key ingredient is that you are creating a task with an async method inside it. Implicitely an async method will always return a task which is complete when the async method has completed. Since you are wrapping the Task with a Task you are waiting on the outer unrelated task which completes immediately hence your race condition.
async void F1()
{
await Task.CompletedTask;
}
async Task F2()
{
await Task.CompletedTask;
}
Both async methods are identical but the F1 method which does return void will still return a task under the hood or else you would not be able to await it. This is just a compiler trick to make "old" code work with async methods, without packing async as special method signature on top of it.
You are creating then tasks like this:
var t = new Task(F1);
var t1 = new Task<Task>(F2);
But now you have wrapped the async task returning method inside an outer task which will be complete as soon as the first sync part of the async method has completed.
What you need to do is to wait on the inner task which can be conveniently done with Task.Unwrap which is there exactly for that reason.
If you remove the Task.Unwrap call in my sample you are waiting on the outer task and you are getting your race condition back.
In general I would not use async/await except to free up the UI thread. async/await is inherently single threaded because you can await always only one task. If used this way (Task.WhenAll is kind of cheating) the one and only feature you get is thread hopping. At a single point in time your async await things will run on one thread which might change before during and after the await depending on the Synchronization Contexts.

Task.Delay unexpected behavior

I have this metod:
public async Task StartAsync(Task process)
{
if (process is null)
{
throw new ArgumentNullException(nameof(process));
}
var loading = ...;
await Task.WhenAll(process, loading).ContinueWith(t => EndProgress());
}
and is called via a command like so:
private async Task TestAsync()
{
await StartAsync(new Task(async () =>
{
//just for testing purpose
await Task.Delay(15000);
}));
}
ExecuteDelegate = async param => await TestAsync();
where ExecuteDelegate is an Action<T> delegate used by command.
Why does the await Task.WhenAll line not waiting those 15 seconds from Task.Dalay?
You need to await the call to StartAsync:
private async Task TestAsync()
{
await StartAsync(new Task(async () =>
{
await Task.Delay(15000);
}));
}
NOTE: You can also simplify your code by not creating the redundant Task:
private async Task TestAsync()
{
await StartAsync(Task.Delay(15000));
}
Or even simpler:
private Task TestAsync()
{
return StartAsync(Task.Delay(15000));
}
You shouldn't use the constructor to create a Task but the static Task.Run method:
private async Task TestAsync()
{
await StartAsync(Task.Run(async () =>
{
//just for testing purpose
await Task.Delay(15000);
}));
}
The task returned by Task.Run can be awaited as expected.

Task.Run fails to process awaited methods

We are running into a situation where have a requirement to start and execute few launch and forget threads during a call. Though, our call fails to execute if the async methods have any awaited call.
Here is an example. Are we missing something?
public class SomeClass
{
public async Task Test()
{
//Calling synchronously this things works
await Save(1).ConfigureAwait(false);
await Save(2).ConfigureAwait(false);
await Save(3).ConfigureAwait(false);
//Starting three threads at the same time fails while trying to run var queryResult = await SomeClient.QueryAsync<T>(q).ConfigureAwait(false);
_ = Task.Run(async () => await Save(1));
_ = Task.Run(async () => await Save(2));
_ = Task.Run(async () => await Save(3));
}
public async Task<bool> Save(int ct)
{
var x = await Update(ct).ConfigureAwait(false);
return x;
}
public async Task<bool> Update(int ct)
{
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
return true;
}
}
public class SomeObject
{
public async Task<T> CallingSomeAsyncMethod(string q)
{
await Task.Delay(1000).ConfigureAwait(false);
//OR Any async method which is awaited here just stops the execution
return queryResult;
}
}
If you want to run multiple tasks at the same time you should call the methods without the await and hold the task. Then you can do await Task.WhenAll(task1, task2, task3, ...);

How to await for without return task method

I am learning about async and await. I have an async method for dialog message and I am doing await to another method.
My code=>
private async Task CalculateProcess()
{
dialogCoordinator = DialogCoordinator.Instance;
// Show...
ProgressDialogController controller = await dialogCoordinator.ShowProgressAsync(this, "HEADER", "MESSAGE");
controller.SetIndeterminate();
// Do your work...
await testing();//I want to await testing method but i cant await testing because testing method no return task
// Close...
await controller.CloseAsync();
}
private void testing()
{
for(int i=0;i<=1000000;i++)
{
}//Looping is example
}
I want to await to testing method but if I want to use await I need to return task from testing.How can I await testing method if I don't have any other returning task?
If you really want to await that method this is the way to go:
private async Task CalculateProcess()
{
dialogCoordinator = DialogCoordinator.Instance;
// Show...
ProgressDialogController controller = await dialogCoordinator.ShowProgressAsync(this, "HEADER", "MESSAGE");
controller.SetIndeterminate();
// Do your work...
await testing();
// Close...
await controller.CloseAsync();
}
private Task testing()
{
for(int i=0;i<=1000000;i++)
{
}//Looping is example
return Task.FromResult(0);
}

How to use Task.WaitAll to wait both tasks

I have the following code which calls 2 async methods and wait for results:
private Task FirstTaskAsync() {
...
}
private Task SecondTaskAsync() {
...
}
private async void Caller() {
await FirstTaskAsync();
await SecondTaskAsync();
}
Problem is it now executes and waits for each task sequentially. I want to change it to Task.WaitAll(). Here is my change:
private async void Caller() {
var first = FirstTaskAsync();
var second = SecondTaskAsync();
Task.WaitAll(first, second);
}
But when I test it, Task.WaitAll() never returns. Can you please tell me what is missing?
First of all, you should never do async void unless you are doing a event handler (which I doubt you are), if you did async Task it makes it more obvious what your problem is.
private async Task Caller() {
await FirstTaskAsync();
await SecondTaskAsync();
}
//This still will not work...
private async Task Caller() {
var first = FirstTaskAsync();
var second = SecondTaskAsync();
Task.WaitAll(first, second);
}
Task.WaitAll returns void and you never use await in the second method, so you are executing the code synchronously and blocking on the SynchronizationContext which will cause you to get deadlocks like you are seeing.
The correct solution would be not to block but instead use Task.WhenAll which returns a Task itself which allows you to drop the async keyword and just return the result
private Task Caller() {
var first = FirstTaskAsync();
var second = SecondTaskAsync();
return Task.WhenAll(first, second);
}
However, if Caller() really is a event handler you can also do async void and just await the result.
private async void Caller() { //This should only be done if this is a event handler.
var first = FirstTaskAsync();
var second = SecondTaskAsync();
await Task.WhenAll(first, second);
}
Would this work for you?
private async void Caller()
{
Task[] tasks = new Task[2];
tasks[0] = FirstTaskAsync();
tasks[1] = SecondTaskAsync();
await Task.WhenAll(tasks);
}

Categories

Resources