c# - texto - leer archivo secuencial vb net



¿Es posible que un Diccionario en.Net provoque un bloqueo total al leer y escribir en paralelo? (2)

Jugaba con TPL e intentaba averiguar qué tan grande podía hacer un lío leyendo y escribiendo en el mismo Diccionario en paralelo.

Así que tuve este código:

    private static void HowCouldARegularDicionaryDeadLock()
    {
        for (var i = 0; i < 20000; i++)
        {
            TryToReproduceProblem();
        }
    }

    private static void TryToReproduceProblem()
    {
        try
        {
            var dictionary = new Dictionary<int, int>();
            Enumerable.Range(0, 1000000)
                .ToList()
                .AsParallel()
                .ForAll(n =>
                {
                    if (!dictionary.ContainsKey(n))
                    {
                        dictionary[n] = n; //write
                    }
                    var readValue = dictionary[n]; //read
                });
        }
        catch (AggregateException e)
        {
            e.Flatten()
                .InnerExceptions.ToList()
                .ForEach(i => Console.WriteLine(i.Message));
        }
    }

De hecho, estaba bastante desordenado, hubo muchas excepciones lanzadas, la mayoría sobre la clave no existe, algunas sobre el índice fuera del límite de la matriz.

Pero después de ejecutar la aplicación durante un tiempo, se bloquea y el porcentaje de CPU se mantiene en un 25%, la máquina tiene 8 núcleos. Así que asumo que son 2 hilos que se ejecutan a plena capacidad.

Luego corrí dottrace en él, y conseguí esto:

Se ajusta a mi conjetura, dos hilos que se ejecutan al 100%.

Ambos ejecutan el método FindEntry del Diccionario.

Luego volví a ejecutar la aplicación, con dottrace, esta vez el resultado es ligeramente diferente:

Esta vez, un hilo está ejecutando FindEntry, el otro Insertar.

Mi primera intuición fue que está bloqueado, pero luego pensé que no podía ser, solo hay un recurso compartido y no está bloqueado.

Entonces, ¿cómo debería explicarse esto?

ps: No busco resolver el problema, podría solucionarse utilizando un ConcurrentDictionary o haciendo una agregación paralela. Sólo estoy buscando una explicación razonable para esto.


Answer #1

Así que tu código está ejecutando Dictionary.FindEntry . No es un interbloqueo: un interbloqueo se produce cuando dos subprocesos bloquean de una manera que los hace esperar unos a otros para liberar un recurso, pero en su caso, obtiene dos bucles aparentemente infinitos. Los hilos no están bloqueados.

Echemos un vistazo a este método en la fuente de referencia :

private int FindEntry(TKey key) {
    if( key == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

    if (buckets != null) {
        int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
        for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
            if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
        }
    }
    return -1;
}

Echa un vistazo al bucle for . La parte de incremento es i = entries[i].next , y adivina qué: las entries son un campo que se actualiza en el método Resize . next es un campo de la estructura de Entry interna:

public int next;        // Index of next entry, -1 if last

Si su código no puede salir del método FindEntry , la causa más probable sería que haya logrado desordenar las entradas de tal manera que produzcan una secuencia infinita cuando siga los índices señalados en el next campo.

En cuanto al método Insert , tiene un bucle muy similar for :

for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next)

Como se documenta que la clase de Dictionary no es segura para subprocesos, de todos modos se encuentra en el ámbito del comportamiento indefinido.

El uso de un ConcurrentDictionary o un patrón de bloqueo como ReaderWriterLockSlim (el Dictionary es seguro para subprocesos solo para lecturas concurrentes) o un lock antiguo simple soluciona el problema.


Answer #2

Parece una condición de carrera (no un punto muerto), que, como comentan, provoca el estado interno desordenado.

El diccionario no es seguro para subprocesos, por lo que las lecturas y escrituras simultáneas en el mismo contenedor de subprocesos separados (incluso si hay tan pocos como uno de cada uno) no son seguros.

Una vez que se alcanza la condición de carrera, se vuelve indefinido lo que sucederá; en este caso, lo que parece ser un bucle infinito de algún tipo.

En general, una vez que se requiere acceso de escritura, se requiere algún tipo de sincronización.





task-parallel-library