dra*_*707 31 c# multithreading volatile
Joe Albahari有一个关于多线程的伟大系列,这是一本必须阅读的内容,对于任何进行C#多线程处理的人来说都应该被人们所熟知.
然而,在第4部分中,他提到了易变的问题:
请注意,应用volatile不会阻止写入后读取交换,这可以创建脑筋急转弯.Joe Duffy通过以下示例很好地说明了这个问题:如果Test1和Test2在不同的线程上同时运行,则a和b最终都可能为0(尽管在x和y上都使用了volatile)
接下来是MSDN文档不正确的说明:
MSDN文档指出使用volatile关键字可确保始终在字段中显示最新值.这是不正确的,因为正如我们所见,可以重新排序读取后跟读取.
我查看了MSDN文档,该文档最后一次在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; }
}
Run Code Online (Sandbox Code Playgroud)
关于多线程的部分是在2011年写的,这个论点今天仍然有效吗?是否应该不惜一切代价避免使用volatile或完全内存防护,以防止引入非常难以产生的错误,如上所述甚至依赖于它运行的CPU供应商?
Voo*_*Voo 32
尽管流行的博客帖子声称有这样的事情,但其当前实施中的易变性并未被打破.然而,它被严格指定,并且在字段上使用修饰符来指定内存排序的想法并不那么好(将Java/C#中的volatile与C++的原子规范进行比较,这些规范有足够的时间来学习早期的错误).另一方面,MSDN文章清楚地写了一个没有业务谈论并发性并且完全是虚假的人.唯一合理的选择是完全忽略它.
Volatile 在访问字段时保证获取/释放语义,并且只能应用于允许原子读取和写入的类型.不是更多,而不是更少.这足以有效地实现许多无锁算法,例如非阻塞散列图.
一个非常简单的示例是使用volatile变量来发布数据.感谢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);
}
}
Run Code Online (Sandbox Code Playgroud)
易失性并不容易使用,在大多数情况下,你最好采用更高级别的概念,但是当性能很重要或者你正在实现一些低级数据结构时,volatile可能非常有用.
Cha*_*ana 13
当我阅读MSDN文档时,我相信它说如果你看到变量上的volatile,你不必担心编译器优化会搞砸价值,因为它们会重新排序操作.它并不表示您受到保护,不会因您自己的代码以错误的顺序在不同的线程上执行操作而导致错误.(虽然不可否认,评论并不清楚.)