The last couple of days I've been reading about async/await. Yesterday I found this video on Channel 9 what made wonder about some things. Please consider the slide below.
Aside from the issues Lucian Wischik addresses, I wondered about variable assignment. Say we changed the async void
into async Task
, and added await
before the SendData
call. This enables us to get the stream, assign the variable m_GetResponse
, wait for two seconds and print it. But what happens to the variable? It could be written by a different thread than it is read. Do we need some sort of a memory barrier here, make the variable volatile, or perhaps something else? Could it still be null when we print it?
Asynchronous programming can help systems run more effectively, depending on the situation and often prevents long wait times. For example, if a task you want to perform uses a lot of input and output, asynchronous programming lets other tasks run, whereas synchronous programming would create a time block.
An asynchronous function is any function that delivers its result asynchronously – for example, a callback-based function or a Promise-based function. An async function is defined via special syntax, involving the keywords async and await . It is also called async/await due to these two keywords.
C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the Task-based Asynchronous Pattern (TAP).
Await is in an async function to ensure that all promises that are returned in the function are synchronized. With async/await, there's no use of callbacks. try and catch methods are also used to get rejection values of async functions.
In the above example, it is safe to read the m_GetResponse
because assignment will happen in same UI thread given that this is being called from UI.
This is because SynchronizationContext
will be captured and continued when the async method resumes. So it is the same UI thread which writes the field and reading it. This isn't a problem here. Refer my related answer here
If called from non UI context, there is no guarantee that continuations will be ran in same thread. Usually it will be ran in ThreadPool thread. Given that the field read isn't volatile it is possible that you could get the previous value if necessary barriers are not inserted. But you don't need to worry about it because TPL already does this for you.
From the above link
Yes, TPL includes the appropriate barriers when tasks are queued and at the beginning/end of task execution so that values are appropriately made visible
So with TPL, you don't need to worry about memory barriers given that the Tasks are already completed. But if you're creating threads manually(which you shouldn't be doing) and dealing with threads directly --you'll have to insert necessary memory barriers.
Btw, ReadToEnd
is a blocking call. I would not call it in UI thread. I'd use ReadToEndAsync
instead to make your UI thread free. And I'll not use field here; I'll return the value from async method because every method call is just dependent on the parameter so it makes sense to return the value from the method.
So, your method will become something like the following
private async Task<string> SendDataAsync(string url)
{
var request = WebRequest.Create(url);
using(var response = await request.GetResponseAsync());
using(var reader = new StreamReader(request.GetResponseStream());
return await reader.ReadToEndAsync();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With