2011-01-05 6 views
29

Ho il seguente metodo:Il codice C libera un blocco?

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

Ho appena aggiunto il lock(Database) per cercare di sbarazzarsi di alcuni problemi concurancy. Sono curioso però, il yield return libererà il blocco su Database e quindi si bloccherà di nuovo quando andrà per la successiva iterazione? O lo Database rimane bloccato per l'intera durata dell'iterazione?

+1

Vedere "And' finally' ... "in http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+6

Questa è un'idea potenzialmente * estremamente pessima *. È molto difficile controllare l'ordine di blocco quando è possibile eseguire codice arbitrario tra quando viene ottenuto il blocco e rilasciato il blocco. Questo è solo chiedere dei deadlock. –

+0

@Eric Lippert - Non ho molta familiarità con la codifica multithread. (Anche se sto migliorando mentre vado.) Ora so che la "magia" dell'istruzione lock è in realtà solo un blocco "try finally'" e che "yield" mantiene le cose nello scope mentre è in uno stato ceduto. Con questo in mente spostato il blocco per circondare solo il ciclo 'for'. Ciò mi dà la restrizione sull'accesso a SQL Server CE di cui ho bisogno mentre non si blocca durante il tempo di iterazione "off". (Almeno così spero!) – Vaccano

risposta

19

No lo yield return non causa il blocco/sblocco di eventuali blocchi. L'istruzione lock si espande in un blocco try/finally e l'iteratore non lo tratterà diversamente da un esplicito try/finally nel metodo iteratore.

I dettagli sono un po 'più complicato, ma le regole base per quando un blocco finally verrà eseguito all'interno di un metodo iteratore è

  1. Quando l'iteratore viene sospeso e Dispose è chiamato il finally fila di portata nel punto della sospensione verrà eseguito
  2. Quando l'iteratore è in esecuzione e il codice attiverebbe altrimenti un finally il blocco finally viene eseguito.
  3. Quando l'iteratore incontra un'istruzione yield break le finally fila di portata nel punto del yield break verrà eseguito
+0

@Marc ha reso la risposta più esplicita che non sarebbe stata gestita in modo diverso da qualsiasi altro 'try/finally' definito in un metodo iteratore. – JaredPar

+0

Hai modificato? Sono sicuro che è stato meno chiaro un momento fa :) –

+0

@Marc yeah modificato per rendere più esplicito il mio intento dopo aver letto il tuo commento. – JaredPar

5

L'oggetto database sarà bloccato fino al termine iterazione (o iteratore è disposto).

Questo potrebbe portare a periodi eccessivi di blocco e consiglierei di non farlo in questo modo.

+2

+1 e concordo con il periodo di blocco eccessivo. –

2

Il blocco rimane attivo finché non si esce dall'ambito del blocco(). Cedendo non lo fa.

13

La serratura si traduce in try/finally (normale C#)

In blocchi Iterator (aka resa), "finalmente" entra a far parte della realizzazione IDisposable.Dispose() del enumeratore. Questo codice è anche invocato internamente quando si consuma l'ultimo dei dati.

"foreach" chiama automaticamente Dispose(), quindi se si utilizza "foreach" (o regualar LINQ ecc.) Lo sarà sbloccato..

Tuttavia, se il chiamante utilizza GetEnumerator() direttamente (molto raro) e pretende molto leggere tutti i dati e non chiamata Dispose(), allora il blocco non sarà rilasciato.

Dovrei controllare per vedere se o ottiene un finalizzatore; it potrebbe essere rilasciato da GC, ma non scommetterei denaro su di esso.