当前位置:编程学习 > C#/ASP.NET >>

volatile关键字的疑惑,求解释!!

问题1:
int _a;
volatile int _b;
void Get(){
  while(true){
     while(_b==1){
         Console.Write(_a);
         return;
     }
  }
}
读取_b由于volatile的获取语义,因此处理器在获取_b的值时,每次都会从内存中直接读取

并且_a的读取,必须在_b读取之后

我现在不明白的是:如果_a之前寄存器存在它的缓存,那_a取的是缓存还是和_b一样,每次都从内存中直接读取?

问题2:

int _a;
volatile int _b;

void Set(){
  _a=2;
  _b=1;
}

_b的写入由于volatile的释放语义,因此处理器将寄存器中_b的值,刷新到内存中,并且,对_b的内存写入操作必须等到_a写入内存之后?
那是不是就意味着,再赋予_b可见性的同时,也赋予了_a的可见性?

如果说,上面的那两个问题,如我解释的那样,那volatile的作用,是不是不单单只作用于被修饰的变量了?可是可能会影响 volatile变量操作之前和之后的变量?
多线程 volatile --------------------编程问答-------------------- 我菜鸟 帮顶! --------------------编程问答--------------------

int inited = 0;
int value  = 0;

// thread 1
{
   value = 42;
   inited = 1;
}

// thread 2
{
   int ini = inited;
   int val = value;
}

从逻辑上,我们期待线程2如果得到ini为1,那么val就一定是42。

但是,在多处理器环境下,该期待(顺序一致性)不一定能得到满足。
首先假设inited和value不在同一个缓存线上,再假设线程2运行在CPU2上,而CPU2可能由于其它操作,value已经被装入缓存了,而inited还没有装入。
这种情况下,线程2就会用内存中读入inited,而从缓存中读入value。也就是说,有可能出现ini=1(从内存来),而val=0(从缓存来)的错误情况。

为了保证'顺序一致性',.NET Framework ECMA标准规定两条violatile规则:
1、读写操作不能被提前到volatile read前面。
2、读写操作不能被延迟到volatile write后面。

也就是说,如果把int inited换成volatile int inited。那么线程2中的int ini = inited就是volatile read。根据规则一,读value的操作不能被提前(不能使用提前缓存的值),这保证了value的值必须从内存中来(缓存失效)。这也就保证了如果ini=1,val一定是42。

至于你的问题,“volatile的作用,是不是不单单只作用于被修饰的变量”。根据规则1和规则2,答案是很明显的。volatile会影响其他读写,因为这就是它的设计目的。 --------------------编程问答-------------------- 这种问题应该根据文档来,而不是自己去试验。

因为CLR可以不断优化,从而在不同的版本和不同的硬件上表现出差异。 --------------------编程问答-------------------- 顺便说下,问题的根本不是寄存器,而是缓存。你可以找一个具有Hyper-Threading的单核心Pentium 4处理器,尽管它拥有2个逻辑CPU,并且每个线程拥有独立的寄存器上下文,但是即便你不用volatile,也不会出现同步问题。 --------------------编程问答--------------------
引用 2 楼 gomoku 的回复:

int inited = 0;
int value  = 0;

// thread 1
{
   value = 42;
   inited = 1;
}

// thread 2
{
   int ini = inited;
   int val = value;
}

从逻辑上,我们期待线程2如果得到ini为1,那么val就一定是42。

但是,在多处理器环境下,该期待(顺序一致性)不一定能得到满足。
首先假设inited和value不在同一个缓存线上,再假设线程2运行在CPU2上,而CPU2可能由于其它操作,value已经被装入缓存了,而inited还没有装入。
这种情况下,线程2就会用内存中读入inited,而从缓存中读入value。也就是说,有可能出现ini=1(从内存来),而val=0(从缓存来)的错误情况。

为了保证'顺序一致性',.NET Framework ECMA标准规定两条violatile规则:
1、读写操作不能被提前到volatile read前面。
2、读写操作不能被延迟到volatile write后面。

也就是说,如果把int inited换成volatile int inited。那么线程2中的int ini = inited就是volatile read。根据规则一,读value的操作不能被提前(不能使用提前缓存的值),这保证了value的值必须从内存中来(缓存失效)。这也就保证了如果ini=1,val一定是42。

至于你的问题,“volatile的作用,是不是不单单只作用于被修饰的变量”。根据规则1和规则2,答案是很明显的。volatile会影响其他读写,因为这就是它的设计目的。


感谢你的回答,你在这里所描述的这段话“读value的操作不能被提前(不能使用提前缓存的值),这保证了value的值必须从内存中来(缓存失效)。这也就保证了如果ini=1,val一定是42"
其实我理解,我的问题1中的疑问所在重点不是这里,不知道兄弟你有没有注意我的例子
我是:
void Get(){
  while(true){
     while(_b==1){
         Console.Write(_a);
         return;
     }
  }
}
我这里有个while(true)
我这里先假设这里顺序
第一次进入循环,read _a不能在read _b之前,_b肯定是从内存直接读取的,此时应该有个cache_b,_a也是从内存中读取的,并且也有个cache_a
第二次进入循环,read _a同样不能在read _b之前,此时 ,_b仍然是从内存中直接读取,而不是cache_b
现在重点来了,_a此时是从cache_a中读取还是从内存中读取 ??非常期待你的回答!
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,