2012-08-01 55 views
1

我有這樣的代碼:鎖聲明飽和

class Program 
{ 
    static void Main(string[] args) 
    { 
     TestClass instanceOfClass = new TestClass(); 
     while (true) 
     { 
      Thread threadTest = new Thread(new ParameterizedThreadStart(AddNewToClass)); 
      threadTest.Start(instanceOfClass); 
     } 
    } 
    static void AddNewToClass(object parameter) 
    { 
     var instance = (TestClass)parameter; 
     while (true) 
     { 
      if (instance.Contains(1)) 
      { 
       continue; 
      } 
      else 
      { 
       instance.AddNew(1); 
      } 
     } 
    } 
} 

class TestClass 
{ 
    public Dictionary<int, string> dictionary; 
    public TestClass() 
    { 
     dictionary = new Dictionary<int, string>(); 
    } 
    public void AddNew(int test) 
    { 
     lock (dictionary) 
     { 
      dictionary.Add(test, "Test string"); 
     } 
    } 
    public bool Contains(int test) 
    { 
     lock (dictionary) 
     { 
      if (dictionary.ContainsKey(test)) 
      { 
       return true; 
      } 
      else 
      { 
       return false; 
      } 
     } 
    } 
} 

我想做的事情,就是有從字典添加/刪除對象中有數個不同的線程。我試着運行這個,我得到這個異常:

具有相同密鑰的項目已被添加。

這看起來非常奇怪。據我所知,鎖定語句應該阻止有問題的字典和TestClass.Contains(1)應該總是返回true,並且它拋出一個異常,因爲它多次返回true(因此是例外)。

任何人都知道爲什麼會發生這種情況?感謝

回答

3

您的Contains()方法是原子的。你的Add()方法也是如此。然而,AddNewToClass()不是。一個線程可能會得到Contains()的結果......但不能保證它何時可能會或可能不會被暫停(或恢復)。

這是你的競爭條件。

+0

謝謝。不知道這件事 – Pacha 2012-08-01 01:59:33

2

你鎖只保護其圍繞塊 - 正是這一點需要保護

static void AddNewToClass(object parameter) 
    { 
     var instance = (TestClass)parameter; 
     while (true) 
     { 
      if (instance.Contains(1)) 
      { 
       continue; 
      } 
      else 
      { 
       instance.AddNew(1); 
      } 
     } 
    } 

if (instance.Contains(1)),你可以得到搶佔instance.AddNew(1);之間。

如果你的東西去像instance.AddItemIfMissing(1);

public void AddItemIfMissing(int test) 
{ 
    lock (dictionary) 
    { 
     if (!dictionary.ContainsKey(test)) 
     { 
      dictionary.Add(test, "Test string"); 
     } 
    } 
} 

這將做你想要的。

0
static void AddNewToClass(object parameter) 
{ 
    var instance = (TestClass)parameter; 
    while (true) 
    { 
     if (instance.Contains(1)) 
     { 
      continue; 
     } // **thread switch maybe happens here will cause your problem** 
     else 
     { 
      instance.AddNew(1); 
     } 
    } 
} 

所以下面是更好

lock(instance) 
    { 
     if (instance.Contains(1)) 
     { 
      continue; 
     } // **thread switch maybe happens here will cause your problem** 
     else 
     { 
      instance.AddNew(1); 
     } 
    } 
1

你有一個賽車的狀態。鎖定後,您需要再次檢查字典是否已使用同一個鍵包含項目,因爲另一個線程在獲取鎖定之前可能已添加了該項目。但爲什麼重新發明輪子? Parallel Extensions庫中有許多助手類,如ConcurrentBag。或者使用Singleton Pattern的思想。