2012-08-01 57 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的思想。