当前位置:  开发笔记 > 编程语言 > 正文

我们需要在多线程代码中读取.NET Int32时锁定它?

如何解决《我们需要在多线程代码中读取.NETInt32时锁定它?》经验,为你挑选了2个好方法。

我正在阅读以下文章:http: //msdn.microsoft.com/en-us/magazine/cc817398.aspx "解决你的多线程代码中的11个可能的问题"作者:Joe Duffy

它提出了一个问题:"我们需要在用多线程代码读取它时锁定.NET Int32吗?"

据我所知,如果它是32位SO中的Int64,它可能会撕裂,正如文章中所解释的那样.但对于Int32,我想到了以下情况:

class Test
{
  private int example = 0;
  private Object thisLock = new Object();

  public void Add(int another)
  {
    lock(thisLock)
    {
      example += another;
    }
  }

  public int Read()
  {
     return example;
  }
}

我没有看到在Read方法中包含锁的原因.你呢?

更新基于答案(由Jon Skeet和ctacke提供)我理解上面的代码仍然容易受到多处理器缓存的影响(每个处理器都有自己的缓存,与其他处理器不同步).所有这三个修改都解决了这个问题:

    添加"int example"的"volatile"属性

    插入Thread.MemoryBarrier(); 在实际读取"int example"之前

    在"lock(thisLock)"中读取"int example"

而且我也认为"易变"是最优雅的解决方案.



1> Jon Skeet..:

锁定完成两件事:

它充当互斥锁,因此您可以确保一次只有一个线程修改一组值.

它提供了内存屏障(获取/释放语义),可确保一个线程的内存写入在另一个线程中可见.

大多数人理解第一点,但不是第二点.假设您使用了来自两个不同线程的问题中的代码,其中一个线程Add重复调用,另一个线程调用Read.单独的原子性将确保您最终只读取8的倍数 - 如果有两个线程调用Add您的锁定将确保您没有"丢失"任何添加.但是,Read即使在Add多次调用之后,你的线程很可能只读0 .没有任何内存障碍,JIT可以将值缓存在寄存器中,并假设它在读取之间没有变化.内存障碍的关键是确保某些内容真正写入主内存,或者确实从主内存中读取内容.

内存模型可能非常繁琐,但如果你按照每次想要访问共享数据(读取写入)时取出锁定的简单规则,你就可以了.有关更多详细信息,请参阅我的线程教程的波动率/原子性部分.



2> Eddie Velasq..:

这一切都取决于具体情况.处理整数类型或引用时,您可能希望使用System.Threading.Interlocked类的成员.

典型用法如:

if( x == null )
  x = new X();

可以通过调用Interlocked.CompareExchange()来替换:

Interlocked.CompareExchange( ref x, new X(), null);

Interlocked.CompareExchange()保证比较和交换作为原子操作发生.

Interlocked类的其他成员,如Add(),Decrement(),Exchange(),Increment()Read()都以原子方式执行各自的操作.阅读MSDN上的文档.

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有