volatile作为同步机制

假设我有一个类Foo,它有一个名为Count的静态成员变量(类型为整数)。 此变量用于multithreading应用程序,我在对此变量进行任何读/写操作之前使用锁同步机制。 正如我正在阅读本文的易变性我得到的印象是我可以删除围绕此变量的所有锁定,并在声明此变量时使用volatile关键字。 这应该照顾所有与synchnronization相关的东西。 它是否正确? 这种方法有哪些优缺点?

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

也许。 也许不吧。 获取multithreading代码非常困难。 获得低锁multithreading代码最好留给专家。

这种方法有哪些优缺点?

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

当您的性能分析使您得出低锁定解决方案是实现客户要求您达到的性能目标的唯一途径时,您应该只使用低锁定解决方案。 当您对每个优化都有深入了解时,您应该只使用低锁定解决方案,您的程序将运行的每个可能的CPU都可以执行低锁定代码。 您需要深入了解CLR保证的内存模型,硬件保证的内容以及所有差异。

我自己并不具备这种理解。 这就是为什么除了最琐碎的低锁代码之外我什么也写不出来的东西,而且我还有行业领先专家认真阅读过的低锁代码。

如果你所做的只是从多个线程读取该变量并从一个线程写入它,那么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

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

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