2011-01-05 19 views
29

que tienen el siguiente método:¿El C# Yield libera un bloqueo?

public static IEnumerable<Dictionary<string, object>> GetRowsIter 
    (this SqlCeResultSet resultSet) 
{ 
    // Make sure we don't multi thread the database. 
    lock (Database) 
    { 
     if (resultSet.HasRows) 
     { 
      resultSet.Read(); 

      do 
      { 
       var resultList = new Dictionary<string, object>(); 
       for (int i = 0; i < resultSet.FieldCount; i++) 
       { 
        var value = resultSet.GetValue(i); 
        resultList.Add(resultSet.GetName(i), value == DBNull.Value 
                    ? null : value); 
       } 
       yield return resultList; 
      } while (resultSet.Read()); 
     } 
     yield break; 
    } 

acabo añade el lock(Database) para tratar de deshacerse de algunas cuestiones concurancy. Sin embargo, tengo curiosidad, ¿el yield return liberará el bloqueo en Database y luego volverá a bloquearse cuando sea necesario para la próxima iteración? ¿O Database permanecerá bloqueado durante toda la iteración?

+1

Consulte "Y' finalmente' ... "en http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+6

Esta es una idea potencialmente * extremadamente mala *. Es muy difícil controlar el orden de bloqueo cuando puede ejecutar código arbitrario entre cuando se obtiene el bloqueo y se libera el bloqueo. Esto es solo pedir puntos muertos. –

+0

@Eric Lippert - No estoy muy familiarizado con la codificación multiproceso. (Aunque estoy mejorando a medida que avanzo). Ahora sé que la "magia" de la instrucción lock es en realidad solo un bloque 'try finally' y que' yield' mantiene las cosas en el alcance mientras está en un estado cedido. Con esto en mente, moví la cerradura para rodear solo el ciclo 'for'. Eso me da la restricción en el acceso de SQL Server CE que necesito sin bloquear durante el tiempo de iteración "off". (Al menos así lo espero!) – Vaccano

Respuesta

19

Sin la yield return no causará ningún bloqueo a ser liberados/desbloqueado. La declaración lock se expandirá a un bloque try/finally y el iterador no tratará esta forma diferente a una explícita try/finally en el método iterador.

Los detalles son un poco más complicado, pero las reglas básicas para cuando un bloque finally se ejecutará dentro de un método iterador es

  1. Cuando se suspende el iterador y Dispose se llama el finally bloques en alcance en el punto de la suspensión de
  2. se ejecutará
  3. Cuando el repetidor está en funcionamiento y el código de otro modo desencadenar una finallyfinally las carreras de bloque.
  4. Cuando el iterador se encuentra con una declaración yield break los finally bloques en alcance en el punto de la yield break se ejecutará
+0

@Marc hizo que la respuesta fuera más explícita de que se manejaría de manera diferente a cualquier otro 'try/finally' definido en un método de iterador. – JaredPar

+0

¿Editó usted? Estoy seguro de que fue menos claro hace un momento :) –

+0

@Marc Sí, editado para que mi intención sea más explícita después de leer su comentario. – JaredPar

5

El objeto base de datos será bloqueado hasta acabados de iteración (o el iterador está dispuesto).

Esto podría dar lugar a períodos excesivos de bloqueo, y lo recomendaría en contra de hacerlo de esta manera.

+2

+1 y estoy de acuerdo con el período de bloqueo excesivo. –

2

El bloqueo permanece en efecto hasta que llegue fuera del alcance de bloqueo(). Ceder no hace eso.

13

El bloqueo se traduce en try/finally (normal C#)

En Iterator bloques (también conocido como de rendimiento), "por fin" se convierte en parte de la implementación IDisposable.Dispose() del empadronador . Este código también se invoca internamente cuando consume el último de los datos.

"foreach" llama automáticamente a Dispose(), por lo que si consume con "foreach" (o regualar LINQ, etc.) se desbloqueará.

Sin embargo, si el usuario utiliza GetEnumerator() directamente (muy raro) y duerma leer todos los datos y no Desechar llamada() entonces el bloqueo no se dará a conocer.

tendría que comprobar para ver si o para crear una finaliser; Es podría ser lanzado por GC, pero no apostaría dinero en ello.

Cuestiones relacionadas