问题
I was having a look at the section on memory barriers as described in http://www.albahari.com/threading/part4.aspx and attempted to make an async/await version of the example provided under 'Do We Really Need Locks and Barriers?':
public class Program
{
static void Main(string[] args)
{
TestAsync();
Console.ReadKey(true);
}
private static async void TestAsync()
{
bool complete = false;
Func<Task> testFunc = async () =>
{
await Task.Delay(1000);
bool toggle = false;
while (!complete) toggle = !toggle;
};
var task = testFunc();
Thread.Sleep(2000);
complete = true;
await task;
Console.WriteLine("Done");
}
}
When run under release mode without debugging, the program will never finish as per the original threading example it's based off.
However, I was under the impression with async/await because of the way the context is saved it would prevent these kinds of issues. Or do all thread safety rules still apply when using async/await?
回答1:
This is actually a compile optimization issue. When you compile in release for some reason it predicts complete will never be true and infinitely runs your application. Since you've based this on another example I'm guessing you already knew that. But as far as the async / await it can't be blamed.
To make this work you would still need to set complete as a volatile variable like so:
static volatile bool complete = false;
This will tell the compiler to check it every cycle regardless and it will work.
I'm not saying I agree with it but what's happening is the compiler is seeing complete unchanged all the way up to the point of the while(!complete) section and since there is no volatile keyword it decides it's never going to change to optimize performance.
Another way to make this work is to remove the compiler optimizations. You can click on the project Properties and then the Build tab and uncheck 'Optimize code'. Then it will work in release.
回答2:
The problem is that access to the complete
variable is not synchronized. await
will insert barriers before and after the task runs, to make sure that the task continuation sees new values, but notice that in this example that won't help. After complete = true
, the fact that the task is awaited does not insert a barrier, and anyway, the looping thread is not synchronizing on the variable read either.
The compiler is allowed to optimize the code, but, even if not, the code might also run forever in practice, as the value of the complete
variable is never explicitly made visible between the two threads.
This can be solved by synchronizing methods, like 'volatile'.
来源:https://stackoverflow.com/questions/41928139/thread-safety-on-async-await-with-memory-caching