乔·阿尔巴哈里有一个很棒的系列 http://www.albahari.com/threading/关于多线程,这是必读的内容,对于任何从事 C# 多线程处理的人来说都应该牢记于心。
然而,在第 4 部分中,他提到了 volatile 的问题:
请注意,应用 volatile 并不会阻止随后执行的写入操作
从交换中读取,这可能会产生脑筋急转弯。乔·达菲
下面的例子很好地说明了这个问题:如果 Test1 和
Test2 在不同线程上同时运行,有可能
b 都以 0 值结束(尽管在
x 和 y)
随后附注MSDN文档不正确:
MSDN 文档指出,使用 volatile 关键字可确保
现场始终存在最新值。
这是不正确的,因为正如我们所见,先写后读可以
被重新排序。
我已经检查过MSDN 文档 https://msdn.microsoft.com/en-us/library/x13ttww7.aspx,最后一次更改是在 2015 年,但仍然列出了:
volatile 关键字表示字段可能会被修改
同时执行的多个线程。字段是
声明的 volatile 不受编译器优化的影响
假设由单线程访问。这确保了最
现场始终存在最新值.
现在我仍然避免使用 volatile,而是使用更详细的方式来防止线程使用过时的数据:
private int foo;
private object fooLock = new object();
public int Foo {
get { lock(fooLock) return foo; }
set { lock(fooLock) foo = value; }
}
由于有关多线程的部分是在 2011 年编写的,所以这个论点今天仍然有效吗?是否仍应不惜一切代价避免使用易失性,转而使用锁或完整内存栅栏,以防止引入非常难以产生的错误(如上所述,这些错误甚至依赖于其运行的 CPU 供应商)?
当前实施中的不稳定因素是not尽管流行的博客文章声称有这样的事情,但还是坏了。然而,它的指定很糟糕,并且在字段上使用修饰符来指定内存排序的想法并不是那么好(将 Java/C# 中的 volatile 与 C++ 的原子规范进行比较,后者有足够的时间从早期的错误中吸取教训)。另一方面,MSDN 文章显然是由一个无权谈论并发的人写的,完全是伪造的。唯一明智的选择就是完全忽略它。
不稳定的担保获取/释放语义访问该字段时,只能应用于允许的类型原子读写。不多也不少。这足以有效地实现许多无锁算法,例如非阻塞哈希图 https://github.com/boundary/high-scale-lib.
一个非常简单的示例是使用易失性变量来发布数据。由于 x 上的 volatile,以下代码片段中的断言无法触发:
private int a;
private volatile bool x;
public void Publish()
{
a = 1;
x = true;
}
public void Read()
{
if (x)
{
// if we observe x == true, we will always see the preceding write to a
Debug.Assert(a == 1);
}
}
易失性并不容易使用,在大多数情况下,您最好采用一些更高级别的概念,但是当性能很重要或者您正在实现一些低级数据结构时,易失性可能非常有用。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)