异步/等待线程安全与内存缓存

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步/等待线程安全与内存缓存相关的知识,希望对你有一定的参考价值。

我正在查看http://www.albahari.com/threading/part4.aspx中描述的关于内存屏障的部分,并尝试制作“我们真的需要锁和障碍吗?”下提供的示例的异步/等待版本:

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");
    

在没有调试的情况下在释放模式下运行时,程序将永远不会按照它基于的原始线程示例完成。

但是,由于上下文保存的方式,我在async / await的印象下会阻止这些问题。或者在使用async / await时是否仍然适用所有线程安全规则?

答案

这实际上是一个编译优化问题。当您在发布中编译时出于某种原因,它预测完成将永远不会是真的并无限运行您的应用程序。既然你是基于另一个例子,我猜你已经知道了。但就async / await而言,它不能归咎于它。

要完成这项工作,您仍然需要将complete设置为volatile变量,如下所示:

        static volatile bool complete = false;

这将告诉编译器每个周期检查它,无论它是否有效。

我不是说我同意它,但正在发生的事情是编译器看到完全没有变化直到while(!complete)部分,并且因为没有volatile关键字它决定它永远不会改变为优化性能。

另一种使这项工作的方法是删除编译器优化。您可以单击项目“属性”,然后单击“构建”选项卡,并取消选中“优化代码”。然后它将在发布中工作。

另一答案

问题是访问complete变量不同步。 await将在任务运行之前和之后插入障碍,以确保任务延续看到新值,但请注意,在此示例中,这将无济于事。在complete = true之后,等待任务的事实并没有插入障碍,无论如何,循环线程也没有同步读取变量。

允许编译器优化代码,但即使没有,代码也可能在实践中永远运行,因为complete变量的值永远不会在两个线程之间显式可见。

这可以通过同步方法来解决,比如'volatile'。

以上是关于异步/等待线程安全与内存缓存的主要内容,如果未能解决你的问题,请参考以下文章

基于信号量与环形队列实现读写异步缓存队列

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

并发与并行同步与异步,线程安全的实现

java并发编程:管程内存模型无锁并发线程池AQS原理与锁线程安全集合类并发设计模式