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;
            }
        }
    }
}

我想做的是有几个不同的线程从字典中添加/删除对象。我试着运行它,我得到了这个异常:

已添加具有相同密钥的项目。

这似乎非常奇怪。据我所知,lock 语句应该阻塞有问题的字典,并且 TestClass.Contains(1) 应该总是返回 true,并且它抛出一个异常,因为它不止一次返回 true(因此是异常)。

任何人都知道为什么会发生这种情况?谢谢

4

4 回答 4

3

你的Contains()方法是原子的。你的Add()方法也是。AddNewToClass(),然而,不是。一个线程可能会从Contains()...得到结果,但不能保证它何时可能会或可能不会被暂停(或恢复)。

那是你的比赛条件。

于 2012-08-01T00:56:17.033 回答
2

你的锁只保护它周围的块 - 正是这个需要保护

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

在 theif (instance.Contains(1))和 the之间instance.AddNew(1);你可以被抢占。

如果你带着类似的东西去instance.AddItemIfMissing(1);

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

这会做你想要的。

于 2012-08-01T00:54:48.640 回答
1

你有一个赛车条件。锁定后,您需要再次检查字典是否已经包含相同键的项目,因为另一个线程可能在您获得锁定之前添加了它。但为什么要重新发明轮子呢?Parallel Extensions 库中有许多帮助类,例如 ConcurrentBag。或者使用经过深思熟虑的单例模式。

于 2012-08-01T00:58:31.533 回答
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);
        }
    }
于 2012-08-01T00:58:18.513 回答