C#中为多线程变量提供原子操作的类Interlocked

最近在工作中经常用到了多线程来处理问题,但是关于多线程共享变量的问题就需要解决了。还好.net为我们提供了InterLocked类,它可是微软专门为多个线程共享的变量提供原子操作的类。我们经常用到的方法之一是Interlocked.Increment()和Interlocked.Decrement()。

如下是MSDN上关于这2个方法的介绍:

IncrementDecrement 方法递增或递减变量并将结果值存储在单个操作中。在大多数计算机上,增加变量操作不是一个原子操作,需要执行下列步骤:

将实例变量中的值加载到寄存器中。

增加或减少该值。

在实例变量中存储该值。

如果不使用 IncrementDecrement,线程会在执行完前两个步骤后被抢先。然后由另一个线程执行所有三个步骤。当第一个线程重新开始执行时,它覆盖实例变量中的值,造成第二个线程执行增减操作的结果丢失。

关于Interlocked类的更详细介绍我们可以参考MSDN上的介绍:

%28v=VS.100%29.aspx。

其实我们还有另外一直方法在多线程中锁定变量或者文件等进行操作,如下所示:

lock ()
{

//do something...

}

通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:如果实例可以被公共访问,将出现C# lock this问题。如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。来看看C# lock this问题:如果有一个类Class1,该类有一个方法用lock(this)来实现互斥:

publicvoidMethod2()  {  lock(this)  {  System.Windows.Forms.MessageBox.Show("Method2End");  }  

如果在同一个Class1的实例中,该Method2能够互斥的执行。但是如果是2个Class1的实例分别来执行Method2,是没有互斥效果的。因为这里的lock,只是对当前的实例对象进行了加锁。

Lock(typeof(MyType))锁定住的对象范围更为广泛,由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例,微软现在建议,不要使用lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。

锁住一个字符串更为神奇,只要字符串内容相同,就能引起程序挂起。原因是在.NET中,字符串会被暂时存放,如果两个变量的字符串内容相同的话,.NET会把暂存的字符串对象分配给该变量。所以如果有两个地方都在使用lock(“my lock”)的话,它们实际锁住的是同一个对象。到此,微软给出了个lock的建议用法:锁定一个私有的static 成员变量。

.NET在一些集合类中(比如ArrayList,HashTable,Queue,Stack)已经提供了一个供lock使用的对象SyncRoot,用Reflector工具查看了SyncRoot属性的代码,在Array中,该属性只有一句话:return this,这样和lock array的当前实例是一样的。ArrayList中的SyncRoot有所不同

get  {  if(this._syncRoot==null)  {  Interlocked.CompareExchange(refthis._syncRoot,newobject(),null);  }  returnthis._syncRoot;要特别注意的是MSDN提到:从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合: QueuemyCollection=newQueue();  lock(myCollection.SyncRoot){  foreach(ObjectiteminmyCollection){  //Insertyourcodehere.  }  }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wydssp.html