关于C#中InterLocked.CompareExchange前读取值的疑问
关于C#中InterLocked.CompareExchange与volatile的疑问解答
咱们先拆解你代码里的两个核心疑问:
1. currVal = this.myVal能否读取到myVal的最新值?
在多线程场景下,这行代码不一定能拿到最新值。
原因是现代CPU和编译器会做缓存优化:线程可能会把myVal的值缓存到自己的本地寄存器或CPU缓存中,而非每次都从主内存读取。如果其他线程修改了主内存里的myVal,当前线程可能还在读取缓存里的旧值,导致currVal拿到的不是变量的最新状态。
2. 是否需要为myVal添加volatile修饰符?
必须加。
volatile关键字的作用就是告诉编译器和CPU:这个变量是“易变”的,不能对它做缓存优化——每次读取必须直接从主内存获取,每次写入必须立刻刷新到主内存。这样才能保证currVal = this.myVal这行读取操作,总能拿到其他线程修改后的最新值。
补充说明:InterLocked.CompareExchange的局限性
你提到InterLocked.CompareExchange是原子操作,这点没错,但它只保证自身的原子性(比较+交换的整个过程不会被打断),却无法保证之前读取myVal的操作的可见性。如果没有volatile,即使CompareExchange做了原子更新,当前线程的currVal可能还是旧值,会导致do-while循环的逻辑出错——比如明明其他线程已经更新了myVal,但当前线程还在基于旧值计算newVal,循环无法正确退出。
所以修正后的代码应该给myVal加上volatile修饰:
class MyObj { // 一些类代码.. volatile int myVal; // 添加volatile修饰符 public void foo() { int currVal = 0, newVal = 0; do { currVal = this.myVal; newVal = this.newValue(currVal); } while (currVal != Interlocked.CompareExchange(ref this.myVal, newVal, currVal)); } }
内容的提问来源于stack exchange,提问作者Ashish Negi
相关产品推荐
相关产品推荐

