2010-01-22 54 views
2

我有幾個數據成員的類:C#嵌套同步

public class Foo 
{ 
    List<int> intLst; 
    int Whatever; 
    HashTable<string> hshTable; 
    ... 
} 

我再有內美孚的功能,它有兩個FOOS並將它們合併成一個新的Foo。我想讓Foo類線程安全。下面的代碼是做到這一點的最佳方式嗎?

public Foo(Foo f) 
{ 
    lock(f) 
    { 
     foreach (int i in f.intLst) 
     { 
      this.intLst.Add(i) 
     } 
     //More complicated 
    } 
} 

public Foo Merge(Foo f1, Foo f2) 
{ 
    Foo fReturn = new Foo(f1);//Using the constructor above 

    lock(f2) 
    { 
     foreach (int i in f2.intLst) 
     { 
      fReturn.intLst.Add(i); 
     } 

     //More complicated merge code 
    } 

    return fReturn; 
} 

這是簡單的嗎?我應該使用監視器嗎?我知道鎖實現了監視器類,但是可能還有一個原因。我也需要擔心在上面的示例中處理死鎖。

我覺得我需要鎖定()f1和f2,因爲如果f2在f1上的複製構造函數期間發生了變化?

+0

順便說一句,列表類型有Union()和Concat()方法... – 2010-01-22 04:35:28

回答

2

是的,就是這麼簡單。

你的代碼是好的。在訪問它之前,您不需要鎖定f2,並且在複製之後不需要鎖定f1。所以實際上沒有必要同時鎖定兩者。

在您的示例中,這些項目本身是不可變的簡單整數,因此不需要鎖定它們,但是如果您有可變項目,則可能需要鎖定各個項目。

由於您一次只持有一個鎖,因此您不必擔心僅使用此代碼造成的死鎖。當然,如果其他代碼在鎖定時調用它,則可能需要擔心死鎖的可能性。

+0

在我的真實應用程序中,項目是可變的。一旦我調用Merge()中的拷貝構造函數,我相信f2在被鎖定之前可能會改變?它是否正確。我想確保一旦調用合併函數,f1和f2都不能改變。我可能只需要一些保證。 – Ames 2010-01-22 05:05:35

+0

您需要考慮的情況是:1.您的代碼複製f1,然後釋放鎖定。 2.另一個線程對f1和f2進行單一更改。 3.你的代碼複製f2然後釋放鎖。在這種情況下,你的代碼將有另一個線程更改之前的f1副本和之後的f2副本。在你的特定應用中,這會不會是一件壞事?如果答案是肯定的,則需要額外的鎖定。如果答案是否定的,你就不會。一般來說,如果兩者都是單一結構的一部分,答案將是肯定的,在這種情況下,您應該鎖定結構。 – 2010-01-22 07:05:53

+0

感謝您的澄清。 – Ames 2010-01-22 07:19:21

0

lock()僅適用於其他lock()塊。因此,只要該代碼也具有鎖定(f),鎖定(f)將阻止其他與f一起工作的代碼,因此鎖定(f)必須在(f)的所有用法上。

您不需要擔心鎖定目標Foo,因爲它仍在構建中 - 沒有其他人可以參考它。