2009-08-24 27 views
1

我有一個實現IList接口的靜態集合。此集合在整個應用程序中使用,包括添加/刪除項目。如何在IList上使用鎖定

由於多線程問題,我不知道我能做些什麼來確保列表一次修改一個,例如當1個線程嘗試添加項目時,另一個線程當時不應該刪除項目。

我想知道lock(this)和lock(privateObject)有什麼不同?我的情況哪一個更好?

謝謝。

+0

集合上的智能鎖定並不是完全直截了當的。賈裏德帕爾有一個很好的[博客文章](http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx),你應該閱讀。 – ScottS 2009-08-24 02:25:32

回答

4

lock(this)將鎖定整個實例,而lock(privateObject)將只鎖定該特定實例變量。第二個是更好的選擇,因爲鎖定整個實例將阻止其他線程能夠對該對象執行任何操作。

MSDN

一般情況下,避免鎖定在公共 類型,或超出你的代碼的 控件實例。共同構建鎖定 (本),鎖(typeof運算(的MyType)),和 鎖( 「myLock」)違反本 方針:

  • 鎖(這)是一個問題,如果 實例可以公開訪問。

  • 鎖(typeof(MyType))是一個問題,如果 MyType是可公開訪問的。

  • 鎖(「myLock」)是一個問題,因爲任何 其他代碼在使用 的過程中使用相同的字符串,將共享相同的鎖。

最佳做法是定義一個私人 對象鎖定,或私有靜態 對象變量,以保護共同 所有實例的數據。

在這種特殊情況下,集合是靜態的這實際上意味着有一個單一實例但依然不改怎麼lock(this)lock(privateObject)會表現。

即使在靜態集合中使用lock(this),仍然會鎖定整個實例。在這種情況下,只要線程A獲取方法Foo()的鎖,所有其他線程將不得不等待對集合執行任何操作。

使用lock(privateObject)意味着只要螺紋一種方法Foo()取得鎖的所有其他線程可以在不等待除了Foo()執行任何其他操作。只有當另一個線程試圖執行方法Foo()時,它將不得不等待線程A完成其Foo()操作並釋放該鎖。

+0

我的收藏是靜態的,這是真的,他們都只是我的應用程序中的一個實例嗎?所以這兩個鎖意味着相同? – thethanghn 2009-08-24 02:23:16

+0

是的,擁有靜態收集意味着應用程序中有效的單個實例,但這不會改變您鎖定的方式。我已經更新了我的答案來解決這個問題。 – 2009-08-24 02:28:44

1

lock關鍵字有點混亂。 lock語句中的對象表達式實際上只是創建關鍵部分的標識機制。它不是鎖的主題,也不是因爲它被聲明引用而以任何方式保證多線程操作的安全。

因此lock(this)正在創建由包含當前正在執行的方法的類標識的關鍵部分,而lock(privateObject)由對該類私有的對象(可能是無論如何)來標識。前者更危險,因爲你的班級的調用者可能會使用lock語句來無意中定義他們自己的關鍵部分,該語句將該類實例用作鎖對象。這可能會導致意外的線程問題,包括但不限於死鎖和瓶頸。

你提到你關心的是多個線程同時修改集合。我應該指出,即使不修改它,你也應該同樣關心讀取集合的線程。在閱讀過程中,您可能需要一些相同的安全警衛來保護收藏,就像在寫作時一樣。

1

將一個私有成員添加到方法鎖定的類中。

例如。

public class MyClass : IList 
{ 
    private object syncRoot = new object(); 

    public void Add(object value) 
    { 
     lock(this.syncRoot) 
     { 
      // Add code here 
     } 
    } 

    public void Remove(object value) 
    { 
     lock(this.syncRoot) 
     { 
      // Remove code here 
     } 
    } 
} 

這將確保訪問列表的同時在線程之間同步添加和刪除個案,同時保持對列表的訪問權限。這仍然會讓枚舉器訪問列表,而另一個線程可以修改它,但是這樣會打開另一個問題,即如果枚舉器在枚舉過程中被修改,枚舉器將拋出異常。