作为同步机制的易失性

本文关键字:易失性 机制 同步机 同步 | 更新日期: 2023-09-27 17:56:15

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

作为同步机制的易失性

我可以删除此变量周围的所有这些锁,并在声明此变量时使用 volatile 关键字。这应该处理所有与同步相关的事情。这是对的吗?

或。也许不是。正确获取多线程代码非常困难。获得正确的低锁多线程代码最好留给专家。

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

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

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

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

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

例如,如果您正在编写以下内容:

Count = Count + 1;

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

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

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

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

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

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