I built some async/await demo console app and get strange result. Code:
class Program
{
public static void BeginLongIO(Action act)
{
Console.
To really understand this behaviour, you need to first understand what Task
is and what async
and await
actually do to your code.
Task
is the CLR representation of "an activity". It could be a method executing on a worker-pool thread. It could be an operation to retrieve some data from a database over a network. Its generic nature allows it to encapsulate many different implementations, but fundamentally you need to understand that it just means "an activity".
The Task
class gives you ways to examine the state of the activity: whether it has completed, whether it has yet to start, whether it generated an error, etc. This modelling of an activity allows us to more easily compose programs which are built as a sequence of activities, rather than a sequence of method calls.
Consider this trivial code:
public void FooBar()
{
Foo();
Bar();
}
What this means is "execute method Foo
, then execute method Bar
. If we consider an implementation that returns Task
from Foo
and Bar
, the composition of these calls is different:
public void FooBar()
{
Foo().Wait();
Bar().Wait();
}
The meaning is now "Start a task using the method Foo
and wait for it to finish, then start a task using the method Bar
and wait for it to finish." Calling Wait()
on a Task
is rarely correct - it causes the current thread to block until the Task
completes and can cause deadlocks under some commonly-used threading models - so instead we can use async
and await
to achieve a similar effect without this dangerous call.
public async Task FooBar()
{
await Foo();
await Bar();
}
The async
keyword causes execution of your method to be broken down into chunks: each time you write await
, it takes the following code and generates a "continuation": a method to be executed as a Task
after the awaited task completes.
This is different from Wait()
, because a Task
is not linked to any particular execution model. If the Task
returned from Foo()
represents a call over the network, there is no thread blocked, waiting for the result - there is a Task
waiting for the operation to complete. When the operation completes, the Task
is scheduled for execution - this scheduling process allows a separation between the definition of the activity and the method by which it is executed, and is the power in the use of tasks.
So, the method can be summarised as:
Foo()
Bar
In your console app, you aren't awaiting any Task
that represents the pending IO operation, which is why you see a blocked thread - there is never an opportunity to set up a continuation which would execute asynchronously.
We can fix your LongIOAsync method to simulate your long IO in an asynchronous fashion by using the Task.Delay()
method. This method returns a Task
that completes after a specified period. This gives us the opportunity for an asynchronous continuation.
public static async Task LongIOAsync()
{
Console.WriteLine("In LongIOAsync start... {0} {1}", (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
Console.WriteLine("In LongIOAsync end... \t{0} {1}", (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, Thread.CurrentThread.ManagedThreadId);
}