Reputation: 9533
In my code sample main thread doesn't wait when task2 is finished.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2();
await Task.Delay(1);
Console.WriteLine("Main 3");
task2.Start();
await task2;
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private Task getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
Result of execution this code is
Main start
task1 start
task1 end
Main 2
Main 3
task2 start
Main end
task2 end
How can I change code where 'Main end' will be at the end of list.
Upvotes: 2
Views: 226
Reputation: 203825
In getTask2
you're starting a Task
that just starts another Task
. So when you await
that Task
it will continue executing as soon as it has finished starting the inner task, not when that inner task has finished.
In this case there's no reason at all for you to create a new task just to start a new task in the first place; you should just use the approach used in the first method.
Of course, if, for some reason, you do need to create a new Task
just to start a new task then you need to get the inner Task
out and ensure that the main method only continues executing when that inner Task
has finished. There is an Unwrap
method to create a Task
that is logically equivalent to a Task
that is the Result
of another Task
:
private Task getTask2()
{
Task<Task> task = new Task<Task>(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
task.Start();
return task.Unwrap();
}
But you don't even need to do that. Task.Run
will automatically unwrap the value for you if it is given a lambda that produces a Task
:
private Task getTask2()
{
return Task.Run(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
And once again, this is all assuming that just starting the asynchronous operation needs to be offloaded to another thread, which should be very rare. Usually this means that there's a bug in that asynchronous method in that it shouldn't be doing long running synchronous work before giving you its Task
in the first place, but on occasion that method will be from an external library that you don't control, so this would be your only option.
Upvotes: 2
Reputation: 13
You have an error in your code. You have not marked the getTask2
method with the keyword async
yet you await
its result. Even though you are using an async
delegate within the method you must use the async
keyword if it is to be successfully awaited.
Here's an idea as to how you can accomplish your desired result.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
await getTask2();
Console.WriteLine("Main end");
}
public async Task AlternateRun()
{
Console.WriteLine("Main start");
List<Task> task_list = new List<Task>();
task_list.Add(getTask1());
task_list.Add(getTask2());
var task_result = Task.WhenAll(task_list);
task_result.Wait();
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private async Task getTask2()
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
I have also added an AlternateRun method that will demonstrate an alternative style of awaiting multiple tasks. If you need your tasks to be completed in order then consider using the ContinueWith
extension method.
Notice that I also removed your task2.Start()
statement. This is already implied by the await
keyword.
Hope this helps.
Upvotes: -1
Reputation: 27871
The constructor overload for Task
that you are using has the following signature:
public Task(Action action)
Although you can assign the following expression to a variable of type Action
:
async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
this action will point to a synchronous function that will synchronously finish once the await
in await Task.Delay(100);
is reached (although the rest of the "action" will continue asynchronously). So, as far as the Task
object is concerned, the execution of this Action
is completed once this await
is reached. This means that the await
in await task2;
will asynchronously continue at that point which explains why "Main end" is printed before "task2 end".
So basically, the constructor for Task
does not support asynchronous actions, just synchronous ones.
To fix this problem, you need to start a task using a method that understands asynchronous actions. An asynchronous action looks like this: Func<Task>
.
The Task.Run
method supports asynchronous actions because it has the following overload:
public static Task Run(Func<Task> function)
So to fix your code, change return new Task(...
to return Task.Run(...
and then don't call the Start
method on the returned task since Task.Run
will create a Task
that is already started.
If you want to delay the execution of the second "task", then I suggest to make the getTask2
method return Func<Task>
(an asynchronous action) instead of Task
like this:
private Func<Task> getTask2()
{
return async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
};
}
And then run such asynchronous action when you want from the Run
method like this:
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2(); //construct asynchronous action
Console.WriteLine("Main 3");
await Task.Run(task2); //execute the asynchronous action
Console.WriteLine("Main end");
}
Upvotes: 1
Reputation: 127603
new Task(
does not work with async functions by itself. You are creating a Task<Task>
, you need to call Unwrap()
before you await the inner task.
private Task<Task> getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
var task2 = getTask2();
Console.WriteLine("Main 3");
task2.Start();
await task2.Unwrap();
Upvotes: 0