volatile 作为同步机制

Posted

技术标签:

【中文标题】volatile 作为同步机制【英文标题】:volatile as a synchronization mechanism 【发布时间】:2011-08-22 15:25:07 【问题描述】:

假设我有一个 Foo 类,它有一个名为 Count 的静态成员变量(类型是整数)。该变量用于多线程应用程序中,我在对该变量进行任何读/写之前使用了锁同步机制。当我阅读这篇文章Volatile 时,我得到的印象是我可以删除这个变量周围的所有锁,并且只在声明这个变量时使用 volatile 关键字。那应该处理所有与同步相关的东西。它是否正确?这种方法的优缺点是什么?

【问题讨论】:

关于volatile的推荐阅读bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx 【参考方案1】:

我可以删除这个变量周围的所有锁,只在声明这个变量时使用 volatile 关键字。那应该处理所有与同步相关的东西。它是否正确?

也许吧。也许不吧。使多线程代码正确是极其困难的。获得正确的低锁多线程代码最好留给专家。

这种方法的优缺点是什么?

优点是避免锁定可以快几纳秒。缺点是,如果你的低锁编程错误,那么你的程序看起来工作得很好,然后会出现无法调试或重现的奇怪故障模式。

只有当您的性能分析得出结论认为低锁定解决方案是实现客户要求您达到的性能目标的唯一方法时,您才应该使用低锁定解决方案.只有当您彻底深入了解您的程序将在其上运行的每个可能的 CPU 都可以在低锁上执行的每个优化时,您才应该使用低锁解决方案-开锁密码。您需要深入了解 CLR 向您保证的内存模型、硬件保证的内容以及所有差异。

我自己并不具备这种理解。这就是为什么除了最琐碎的低锁代码之外我什么都不写,而且我写的低锁代码很少,而且经过行业领先专家的仔细审查。

【讨论】:

【参考方案2】:

如果您所做的只是从多个线程读取该变量并从一个线程写入,那么volatile 可能会起作用。但是,如果您在多个线程上更新该值(即递增它),那么您需要某种同步。

例如,如果你正在写这个:

Count = Count + 1;

请记住,增量需要三个操作:读取、递增、写入。在多个线程上,您可能会遇到问题。假设Count的初始值为零。

线程 1 读取 Count(值 0) 线程 2 读取计数(值 0) 线程 1 递增其值 (1) 线程 1 写入 Count(值 1) 线程 2 递增其值 (1) 线程 2 写入 Count(值 1)

Count 的最终值是 1,而应该是 2。

您想查看Interlocked 类。特别是Interlocked.IncrementInterlocked.CompareExchange

【讨论】:

【参考方案3】:

volatile 关键字与lock 完全不同。 lock 意味着有多个语句需要作为具有一致状态的单个工作单元发生,例如,如果您想确保在访问时没有其他线程可以从集合中添加或删除项目集合中的特定项目或计算集合中的项目。 volatile 关键字会影响编译器在从源代码生成机器指令时尝试优化代码的行为。编译器可能会以不影响该代码块的方式重新排序特定代码块中的语句,但如果您的变量的值可以从另一个线程更改,则重新排序可能无效。使用volatile 告诉编译器不要考虑这种优化,并且总是从内存中读取变量的值,而不是从缓存的寄存器中读取。

Jon Skeet 在他的文章 Volatility, Atomicity and Interlocking 中更详细地讨论了这一点。

【讨论】:

以上是关于volatile 作为同步机制的主要内容,如果未能解决你的问题,请参考以下文章

14volatile(轻量级的同步机制)

14volatile(轻量级的同步机制)

轻量级的同步机制——volatile语义详解(可见性保证+禁止指令重排)

synchronized同步块和volatile同步变量

volatile -- 最轻量级的同步机制

java并发编程原子变量和非阻塞的同步机制