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()ただし、そうではありません。1つのスレッドが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);
            }
        }
    }

との間で、プリエンプトされる可能性があります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");
        }
    }
}

これはあなたが望むことをするでしょう。

于 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 に答える