Reputation: 105
After playing with .NET 4.5 async\await framework I have a question.
Let's look at this program (the msdn example):
async Task<int> AccessTheWebAsync()
{
HttpClient client = new HttpClient();
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
DoIndependentWork();
string urlContents = await getStringTask;
return urlContents.Length;
}
void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
The program will run in this order:
One thing that took me a while to understand is that the method GetStringAsync
runs synchronously despite its name (the name convention is really misleading).
In oreder to run the method asynchronously, we need to use Task.Run
or Task.Factory.StartNew
explicitly.
But the real question is, if we have independent work, why not do it right away rather then waiting the await to be called from GetStringAsync? (In other words, why async methods doesn't run asynchronously by definition?)
EDIT: I'll rephrase the second and third operations:
(2) GetStringAsync start synchronously.
(3) In some point, GetStringAsync calls await and the thread forks, the control return to AccessTheWebAsync.
Upvotes: 5
Views: 5063
Reputation: 14856
Each async
method is split into segments on each await
. Each segment will become a state on a compiler generated state machine.
Each await
instruction works on an awaitable for which a Task
is the most common case.
Each state/segment will be executed synchronously until the where the received awaitable is checked if it is already completed.
If the awaitable is completed, it execution will continue on the next state.
If the awaitable is not completed and there isn't a current SynchronizationContext
, the execution will be blocked until the the awaitable is completed at which time the execution of the next state will be started.
If a current SynchronizationContext
exists, the execution will return to the caller and when the awaitable is completed the continuation to the next state will be posted to the captured SynchronizationContext
.
Upvotes: 3
Reputation: 245036
The program waits the string to be returned (blocks if the operation didn't complete).
No, it doesn't. await
waits, but it doesn't block any threads. That's the whole point.
One thing that took me a while to understand is that the method
GetStringAsync
runs synchronously despite its name (the name convention is really misleading).
That's wrong. Most of the method does run asynchronously. It most likely also has a small synchronous part at the start, but that should be negligible.
In oreder to run the method asynchronously, we need to use
Task.Run
orTask.Factory.StartNew
explicitly.
No, the method already does run asynchronously. If you want to run the small synchronous part on another thread, then you can use Task.Run()
, but that almost never makes any sense. Also, don't use StartNew()
for this, it doesn't work well with async methods.
In other words, why async methods doesn't run asynchronously by definition?
A significant point about await
is that it resumes on the same context where it left.
This can be very useful in GUI applications, where you often want to modify the UI, then execute the async operation and then modify the UI again. If async
automatically meant that the whole method executes on a ThreadPool
thread, then that wouldn't be possible.
Also, doing it this way is more efficient, because you don't have to switch to another thread for something that takes very little time.
Upvotes: 2
Reputation: 61744
The program waits the string to be returned (blocks if the operation didn't complete).
await
doesn't block on await getStringTask
. It will save the internal state of the AccessTheWebAsync
method (local variables and where the execution point is) in a compiler-generated state machine object and will return to the outer method which called AccessTheWebAsync
. The state will be restored and the execution will be resumed later asynchronously when getStringTask
task has become completed, via a compiler-generated continuation callback. If you're familiar with C# iterators and yield
keyword, the await
state machine control flow is very similar to it, albeit iterators are executed synchronously.
How exactly the execution will be resumed after await
depends on the synchronization context of the thread initiating the await
. If it's a UI thread pumping Windows messages, the continuation callback will likely be called on the next iteration of the thread's message loop (typically inside Application.Run
). If it's a non-UI thread (e.g., a console application), the continuation may happen on a different thread at all. So, while the control flow of your method remains logically linear, technically it is not (as it would have been if you just did getStringTask.Wait()
instead of await getStringTask
).
Upvotes: 2
Reputation: 564871
One thing that took me a while to understand is that the method GetStringAsync runs synchronously despite its name (the name convention is really misleading).
That is incorrect. GetStringAsync
returns a Task<string>
. It will return immediately, which means DoIndependentWork
will (potentially) run before the download has completed. The await
operator will asynchronously wait until the Task<T>
returned by GetStringAsync
is done.
But the real question is, if we have independent work, why not do it right away rather then waiting the await to be called from GetStringAsync? (In other words, why async methods doesn't run asynchronously by definition?)
The actual "work" of the asynchronous methods is running after the method has returned. The Task
that's returned will (normally) be in a state that's not completed, which means the method is still running asynchronously. You can check Task.IsCompleted
to verify.
Try this:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true
Upvotes: 8