歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C#中為多線程變量提供原子操作的類Interlocked

C#中為多線程變量提供原子操作的類Interlocked

日期:2017/3/1 10:39:32   编辑:Linux編程
最近在工作中經常用到了多線程來處理問題,但是關於多線程共享變量的問題就需要解決了。還好.net為我們提供了InterLocked類,它可是微軟專門為多個線程共享的變量提供原子操作的類。我們經常用到的方法之一是Interlocked.Increment()和Interlocked.Decrement()。

如下是MSDN上關於這2個方法的介紹:

IncrementDecrement 方法遞增或遞減變量並將結果值存儲在單個操作中。在大多數計算機上,增加變量操作不是一個原子操作,需要執行下列步驟:

  1. 將實例變量中的值加載到寄存器中。

  2. 增加或減少該值。

  3. 在實例變量中存儲該值。

如果不使用 IncrementDecrement,線程會在執行完前兩個步驟後被搶先。然後由另一個線程執行所有三個步驟。當第一個線程重新開始執行時,它覆蓋實例變量中的值,造成第二個線程執行增減操作的結果丟失。

關於Interlocked類的更詳細介紹我們可以參考MSDN上的介紹:

http://msdn.microsoft.com/zh-cn/library/system.threading.interlocked%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)來實現互斥:

  1. publicvoidMethod2()
  2. {
  3. lock(this)
  4. {
  5. System.Windows.Forms.MessageBox.Show("Method2End");
  6. }
  7. }

如果在同一個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有所不同

  1. get
  2. {
  3. if(this._syncRoot==null)
  4. {
  5. Interlocked.CompareExchange(refthis._syncRoot,newobject(),null);
  6. }
  7. returnthis._syncRoot;
要特別注意的是MSDN提到:從頭到尾對一個集合進行枚舉本質上並不是一個線程安全的過程。即使一個集合已進行同步,其他線程仍可以修改該集合,這將導致枚舉數引發異常。若要在枚舉過程中保證線程安全,可以在整個枚舉過程中鎖定集合:
  1. QueuemyCollection=newQueue();
  2. lock(myCollection.SyncRoot){
  3. foreach(ObjectiteminmyCollection){
  4. //Insertyourcodehere.
  5. }
  6. }
Copyright © Linux教程網 All Rights Reserved