Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是';挥发性';关键字仍在C#中损坏?_C#_Multithreading_Volatile - Fatal编程技术网

是';挥发性';关键字仍在C#中损坏?

是';挥发性';关键字仍在C#中损坏?,c#,multithreading,volatile,C#,Multithreading,Volatile,Joe Albahari有一本关于多线程的书,这是一本必读的书,任何从事C#多线程的人都应该牢记这一点 然而,在第4部分中,他提到了volatile的问题: 请注意,应用volatile并不会阻止在写入之后加上 从被交换中读取,这会产生脑筋急转弯。乔·达菲 通过以下示例很好地说明了问题:如果Test1和 Test2在不同的线程上同时运行,并且 b都以值0结束(尽管使用了volatile on) x和y) 然后注意MSDN文档不正确: MSDN文档说明使用volatile关键字可以确保 字段中始终

Joe Albahari有一本关于多线程的书,这是一本必读的书,任何从事C#多线程的人都应该牢记这一点

然而,在第4部分中,他提到了volatile的问题:

请注意,应用volatile并不会阻止在写入之后加上 从被交换中读取,这会产生脑筋急转弯。乔·达菲 通过以下示例很好地说明了问题:如果Test1和 Test2在不同的线程上同时运行,并且 b都以值0结束(尽管使用了volatile on) x和y)

然后注意MSDN文档不正确:

MSDN文档说明使用volatile关键字可以确保 字段中始终存在最新的值。 这是不正确的,因为正如我们所看到的,写操作之后是读操作 重新排序

我已经检查了,上一次更改是在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年写的,这个论点今天仍然有效吗?是否仍应不惜一切代价避免volatile而采用锁或全内存围栏,以防止引入非常难以产生的bug,如前所述,这些bug甚至依赖于运行它的CPU供应商?

当我阅读MSDN文档时,我相信这是在说,如果您在变量上看到volatile,您不必担心编译器优化会破坏值,因为它们会重新排序操作。它并没有说您可以避免由于您自己的代码在不同线程上以错误的顺序执行操作而导致的错误。(尽管无可否认,评论对此并不清楚。)

尽管流行的博客文章声称这一点,但其当前实现中的Volatile并没有被破坏。然而,它的指定很糟糕,在字段上使用修饰符来指定内存顺序的想法也不是很好(将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); 
    }
}

Volatile不易使用,在大多数情况下,您最好使用一些更高级别的概念,但当性能很重要或您正在实现一些低级别的数据结构时,Volatile可能非常有用。

Volatile
是非常有限的保证。这意味着该变量不受假定从单个线程访问的编译器优化的影响。这意味着,如果从一个线程写入变量,然后从另一个线程读取,那么另一个线程肯定会具有最新的值。如果没有volatile,一台多处理器机器没有volatile,编译器可能会对单线程访问进行假设,例如将值保留在寄存器中,这会阻止其他处理器访问最新的值

正如您提到的代码示例所示,它不能保护您不让不同块中的方法重新排序。实际上,
volatile
使每个人都可以访问
volatile
变量原子。它不保证访问组的原子性

如果您只想确保您的属性具有最新的单个值,那么您应该能够只使用
volatile

如果您试图像执行原子操作一样执行多个并行操作,就会出现问题。如果必须将几个操作强制为原子操作,则需要锁定整个操作。再次考虑这个例子,但是使用锁:

class DoLocksReallySaveYouHere
{
  int x, y;
  object xlock = new object(), ylock = new object();

  void Test1()        // Executed on one thread
  {
    lock(xlock) {x = 1;}
    lock(ylock) {int a = y;}
    ...
  }

  void Test2()        // Executed on another thread
  {
    lock(ylock) {y = 1;}
    lock(xlock) {int b = x;}
    ...
  }
}
锁定原因可能会导致某些同步,这可能会阻止
a
b
的值为0(我尚未测试该值)。但是,由于
x
y
都是独立锁定的,因此
a
b
仍然可以不确定地以0值结束


因此,在包装单个变量的修改的情况下,使用
volatile
应该是安全的,而使用
lock
则不会更安全。如果需要以原子方式执行多个操作,则需要在整个原子块周围使用
锁,否则调度仍将导致不确定性行为。

以下是一些对C#中volatile的有用分解:

在x86上,它大约是:

  • 使用内存而不是寄存器
  • 防止编译器优化,如无止境循环

当我只想告诉编译器一个字段可能会从许多不同的线程更新,并且我不需要联锁操作提供的附加功能时,我使用volatile。在您的示例中,围绕
return
lock
的赋值语句有什么意义?这仍然是误导。Volatile提供的获取/释放内存语义是