2 Stimmen

Warum macht die Verwendung von ReaderWriterLockSlim mein Dictionary nicht thread-sicher?

Ich habe ein kleines Stück Code geschrieben, das schnell von mehreren Threads aus in ein Wörterbuch liest und schreibt. Ich habe ReaderWriterLockSlim verwendet, um den Code zu schützen, und trotzdem eine Ausnahme erhalten, weil ich angeblich versucht habe, doppelte Schlüssel hinzuzufügen.

ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
Dictionary<int, int> _dict = new Dictionary<int, int>();

public SafeDictionaryTester()
{
    for (int i = 0; i < 7; i++)
    {
        _dict.Add(i, i);
    }
}

internal void Execute()
{
    for (int i = 7; i < 10000; i++)
    {
        if (i % 6 == 0)
            new Thread(new ThreadStart(delegate { Print(6); })).Start();
        else if (i % 5 == 0)
            new Thread(new ThreadStart(delegate { Print(5); })).Start();
        else if (i % 4 == 0)
            new Thread(new ThreadStart(delegate { Print(4); })).Start();
        else if (i % 3 == 0)
            new Thread(new ThreadStart(delegate { Print(3); })).Start();
        else if (i % 2 == 0)
            new Thread(new ThreadStart(delegate { Print(2); })).Start();
        else if (i % 1 == 0)
            new Thread(new ThreadStart(delegate { Print(1); })).Start();

        new Thread(new ThreadStart(delegate
        {
            _lock.EnterWriteLock();
            try
            {
                _dict.Add(i, i); // Exception after random number of loops
                Console.WriteLine(i.ToString() + " added");
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        })).Start();
    }
}

private void Print(int i)
{
    _lock.EnterReadLock();
    try
    {
        int obj;
        if (_dict.TryGetValue(i, out obj))
        {
            Console.WriteLine(obj);
        }
        else
        {
            throw new Exception();
        }
    }
    finally
    {
        _lock.ExitReadLock();
    }
}

Beachten Sie, dass derselbe Code ohne die Threads einwandfrei ausgeführt wird.

5voto

Jeff Sternal Punkte 46528

Das Problem ist, dass Ihr anonymer Writer-Delegat eine Verschluss über i .

Das heißt, wenn Ihre Writer-Threads ausgeführt werden, verwenden sie der aktuelle Wert von i und nicht den Wert zum Zeitpunkt das Thema wurde gestartet (7, 8, 9 ... usw.)

Um dies zu beheben, müssen Sie eine Kopie der Variablen in Ihrer for-Schleife erstellen und diese in Ihrem Writer-Delegaten verwenden:

internal void Execute()
{
    for (int i = 7; i < 10000; i++)
    {
        // trimmed for brevity: create a copy of i
        int copy = i;

        new Thread(new ThreadStart(delegate
        {
            _lock.EnterWriteLock();
            try
            {
                _dict.Add(copy, copy); // Exception after random number of loops
                Console.WriteLine(copy.ToString() + " added");
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        })).Start();
    }

4voto

Jon Skeet Punkte 1325502

Wie Ani sagt, hat das wenig mit dem Wörterbuch zu tun. Du hast wirklich sind Sie versuchen (wahrscheinlich), denselben Schlüssel zweimal hinzuzufügen, weil Sie die Schleifenvariable erfassen. Die einfache Lösung besteht darin, die Schleifenvariable in eine neue Variable zu kopieren innerhalb der Schleife so dass jeder zusätzliche Thread nur seinen eigenen Wert "sieht".

for (int i = 7; i < 10000; i++)
{
    // Other stuff...
    copyOfI = i;

    new Thread(new ThreadStart(delegate
    {
        _lock.EnterWriteLock();
        try
        {
            _dict.Add(copyOfI, copyOfI);
            Console.WriteLine(copyOfI.ToString() + " added");
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    })).Start();
}

Weitere Informationen finden Sie in den Blogbeiträgen von Eric Lippert: Teil 1 ; Teil 2 .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X