Are you there, asynchronously written value?

时光毁灭记忆、已成空白 提交于 2020-07-03 06:25:33

问题


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.

Variable assignment in task

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?


回答1:


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();
}



回答2:


But what happens to the variable? It could be written by a different thread than it is read.

If m_GetResponse is a private field, and this class gets invoked multiple times by different threads, then yes, it is possible for the value to be "dirty" once someone else tries to read it. In order to make it thread-safe, you could lock around it. It seems that the authors intention was to invoke this from the UI thread only, hence him making SendData a private method. In that case, it is safe for m_GetResponse to be a private field, as the continuation of the async method which is responsible for the variable assignment will happen inside the UI message loop.

Could it still be null when we print it?

It could be null if somewhere else in the code, someone sets that variable to null, as it's a class level variable. If you're talking about "could it be that we try to print m_GetResponse before await finishes the state-machine execution, then no. Again, i'm not sure the authors intentions were made around concurrent execution, but rather to show you async-await features.

or perhaps something else?

In order to make it thread safe, you can simply remove the global variable, and return a local variable instead. SendData shouldn't be async void anyway, as it isn't used for event handler delegate assignment like Button1_Click.

You could do it better like this (i'll use HttpClient for simplicity):

public async Task<string> SendDataAsync(string url)
{
    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync();
    return response.Content.ReadAsStringAsync();
}

Note you should remember that async-await isn't meant to address parallelism, it's more about concurrency and easing the use of naturally async IO operations.



来源:https://stackoverflow.com/questions/28871878/are-you-there-asynchronously-written-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!