我有这段代码:
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;
}
}
}
}
我想要做的是,有几个不同的线程来添加/删除Dictionary中的对象。我试过运行这个,我得到了这个例外:
已添加具有相同键的项目。
这似乎非常奇怪。据我所知,lock语句应该阻止有问题的字典,而TestClass.Contains(1)应该总是返回true,并且它抛出异常,因为它多次返回true(因此异常) )。
任何人都知道为什么会这样?感谢
答案 0 :(得分:3)
您的Contains()
方法是原子的。你的Add()
方法也是如此。但是,AddNewToClass()
不是。一个帖子可能会从Contains()
得到一个结果...但是不能保证它何时可能暂停(或恢复)。
那是你的竞争条件。
答案 1 :(得分: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");
}
}
}
这可以做你想要的。
答案 2 :(得分:1)
你有比赛条件。锁定后,您需要再次检查字典是否已包含相同键的项目,因为在获取锁定之前,另一个线程可能已添加该项目。但为什么重新发明轮子? Parallel Extensions库中有许多辅助类,如ConcurrentBag。或者通过Singleton Pattern仔细考虑。
答案 3 :(得分: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);
}
}