挥发性主场迎战主场迎战互锁锁 [英] Volatile vs. Interlocked vs. lock

查看:165
本文介绍了挥发性主场迎战主场迎战互锁锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设一个类有一个由多个线程访问公众诠释计数字段。这 INT 只递增或递减。

要增加这个领域,应该使用哪种方法,为什么?


  • 锁(this.locker)this.counter ++;

  • Interlocked.Increment(REF this.counter);

  • 修改计数的访问修饰符公共挥发性

现在,我已经发现了挥发性,我已经消除了许多锁定语句和使用互锁。但有一个理由不这样做呢?


解决方案

最差的(实际上不会工作)


  

修改计数的访问修饰符公共挥发性


至于其他人都提到,这对自己是不是真正的安全可言。点挥发性是在多个CPU上运行多个线程可以和将缓存数据和重新排序的说明。

如果它的不可以 挥发性和CPU A增加一个值,那么CPU B实际上可能没有看到递增的值,直到一段时间之后,这可能会引起问题。

如果是挥发性,这才保证了两个CPU看到相同的数据在同一时间。它不会从他们的交错读取阻止他们不惜一切,写这就是问题,你正试图避免操作。

次优:


  

锁(this.locker)this.counter ++ ;


这是安全的(只要你记得锁定其他地方,你访问 this.counter )。它prevents从执行任何其他code这是由更衣室守卫任何其他线程。
使用锁也,prevents多CPU重新排序如上问题,这是伟大的。

问题是,锁定是缓慢的,如果你在其中没有多大关系,那么你就可以结束拦截你的其他线程的一些其他地方重复使用更衣室没有理由。

最佳


  

Interlocked.Increment(REF this.counter);


这是安全的,因为它确实有效的读取,递增,并在一击,这不能被打断写。因此它不会影响任何其他code和你不需要记住要么在其他地方锁定。它也非常快(如MSDN说,在现代的CPU这往往是名副其实的单CPU指令)。

我不是很确定但如果它得到周围其他CPU重新排序的东西,或者如果你还需要与挥发增量相结合。

InterlockedNotes:


  1. 互锁方法是在核心或CPU的任何数量的CONCURRENTLY安全。

  2. 互锁方法适用于一个完整的围墙周围,他们执行指令,所以重新排序不会发生。

  3. 互锁方法的不需要甚至不支持访问volatile字段,挥发性是放置围绕特定领域操作的一半围栏,并使用全栅栏互锁。

脚注:什么波动是真正的优点

由于挥发性没有prevent这几样多线程问题,它是什么呢?一个很好的例子是说你有两个线程,一个总是写入一个变量(比如 queueLength ),并且其中一个总是从同一个变量的读取。

如果 queueLength 不挥发,线程A可以写信五次,但线程B可以看到那些写为被延迟(甚至可能以错误的顺序)。

一个解决办法是锁定的,但你也可以在这种情况下使用volatile。这将确保线程B总会看到最先进的最新东西,线程A写。但是请注意,这个逻辑的只有的,如果你有谁从来不写作家谁从来不看,与读者的作品,的如果你正在写的东西是一个原子值。只要你做一个单一的读 - 修改 - 写,你需要去交错操作或使用锁。

Let's say that a class has a public int counter field that is accessed by multiple threads. This int is only incremented or decremented.

To increment this field, which approach should be used, and why?

  • lock(this.locker) this.counter++;,
  • Interlocked.Increment(ref this.counter);,
  • Change the access modifier of counter to public volatile.

Now that I've discovered volatile, I've been removing many lock statements and the use of Interlocked. But is there a reason not to do this?

解决方案

Worst (won't actually work)

Change the access modifier of counter to public volatile

As other people have mentioned, this on its own isn't actually safe at all. The point of volatile is that multiple threads running on multiple CPUs can and will cache data and re-order instructions.

If it is not volatile, and CPU A increments a value, then CPU B may not actually see that incremented value until some time later, which may cause problems.

If it is volatile, this just ensures the two CPUs see the same data at the same time. It doesn't stop them at all from interleaving their reads and write operations which is the problem you are trying to avoid.

Second Best:

lock(this.locker) this.counter++;

This is safe to do (provided you remember to lock everywhere else that you access this.counter). It prevents any other threads from executing any other code which is guarded by locker. Using locks also, prevents the multi-CPU reordering problems as above, which is great.

The problem is, locking is slow, and if you re-use the locker in some other place which is not really related then you can end up blocking your other threads for no reason.

Best

Interlocked.Increment(ref this.counter);

This is safe, as it effectively does the read, increment, and write in 'one hit' which can't be interrupted. Because of this it won't affect any other code, and you don't need to remember to lock elsewhere either. It's also very fast (as MSDN says, on modern CPUs this is often literally a single CPU instruction).

I'm not entirely sure however if it gets around other CPUs reordering things, or if you also need to combine volatile with the increment.

InterlockedNotes:

  1. INTERLOCKED METHODS ARE CONCURRENTLY SAFE ON ANY NUMBER OF COREs OR CPUs.
  2. Interlocked methods apply a full fence around instructions they execute, so reordering does not happen.
  3. Interlocked methods do not need or even do not support access to a volatile field, as volatile is places a half fence around operations on given field and interlocked is using the full fence.

Footnote: What volatile is actually good for.

As volatile doesn't prevent these kind of multithreading issues, what's it for? A good example is say you have two threads, one which always writes to a variable (say queueLength), and one which always reads from that same variable.

If queueLength is not volatile, thread A may write five times, but thread B may see those writes as being delayed (or even potentially in the wrong order).

A solution would be to lock, but you could also use volatile in this situation. This would ensure that thread B will always see the most up-to-date thing that thread A has written. Note however that this logic only works if you have writers who never read, and readers who never write, and if the thing you're writing is an atomic value. As soon as you do a single read-modify-write, you need to go to Interlocked operations or use a Lock.

这篇关于挥发性主场迎战主场迎战互锁锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆