2011-01-05 14 views
29

Mam następujący sposób:Czy C# wydajność odblokowuje blokadę?

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

Właśnie dodałem do lock(Database) aby spróbować pozbyć się niektórych kwestiach concurancy. Ciekaw jestem jednak, czy yield return zwolni blokadę na Database, a następnie ponownie ją zablokuje, gdy przejdzie do następnej iteracji? Czy też Database pozostanie zablokowane przez cały czas trwania iteracji?

+1

Zobacz "I" wreszcie "..." w http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+6

Jest to potencjalnie * wyjątkowo zły pomysł *. Bardzo trudno jest kontrolować porządkowanie zamków, gdy można uruchomić dowolny kod między momentem uzyskania blokady a zwolnieniem blokady. To tylko pytanie o zakleszczenia. –

+0

@Eric Lippert - Nie jestem zbyt zaznajomiony z kodowaniem wielowątkowym. (Chociaż coraz lepiej mi idzie.) Teraz wiem, że "magia" instrukcji lock jest w rzeczywistości tylko blokiem "try finally", a ta "wydajność" zachowuje rzeczy w zakresie, gdy jest w stanie przyniesionym. Mając to na uwadze, przesunął blokadę, aby otoczyć pętlę 'for'. To daje mi ograniczenie dostępu do SQL Server CE, którego potrzebuję, a nie blokowanie podczas "off" czasu iteracji. (Przynajmniej tak mam nadzieję!) – Vaccano

Odpowiedz

19

Żadne yield return nie spowoduje zwolnienia/odblokowania zamków. Instrukcja lock rozszerzy się do bloku try/finally, a iterator nie potraktuje tego w inny sposób niż jawny try/finally w metodzie iteratora.

Dane są nieco bardziej skomplikowane, ale podstawowe zasady gdy finally blok będzie działać wewnątrz metody iteracyjnej jest

  1. Gdy iterator jest zawieszony i Dispose nazywa się finally bloki w zakresie w punkcie Zawieszenie uruchomi się:
  2. Gdy iterator jest uruchomiony, a kod w przeciwnym razie wyzwoliłby blokadę w trybie finally.
  3. Gdy iteracyjnej napotyka yield break Oświadczenie finally bloków w zakresie w punkcie yield break uruchomi
+0

@Marc udzielił bardziej jednoznacznej odpowiedzi, że byłby obsługiwany nie inaczej niż jakikolwiek inny "try/finally" zdefiniowany w metodzie iteratora. – JaredPar

+0

Czy edytowałeś? Jestem pewien, że to było mniej oczywiste przed chwilą :) –

+0

@Marc tak zredagowane, aby moje intencje były bardziej wyraźne po przeczytaniu twojego komentarza. – JaredPar

5

Przedmiotem bazy danych zostanie zablokowana aż do wykończenia iteracji (lub iteracyjnej jest umieszczona).

Może to prowadzić do nadmiernych okresów blokowania i zalecam, aby tego nie robić.

+2

+1 i zgadzam się z nadmiernym czasem blokowania. –

2

Zamek pozostaje w mocy, dopóki nie wyjdziesz poza zasięg blokady(). Plonowanie tego nie robi.

13

Blokada przekłada spróbować wreszcie (normalny/C#)

w blokach iterator (aka wydajności), "w końcu" staje się częścią IDisposable.Dispose() realizacji wyliczający. Ten kod jest również wywoływany wewnętrznie, gdy zużywa się ostatni z danych.

"foreach" automatycznie wywołuje Dispose(), więc jeśli spożyjesz z "foreach" (lub regualar LINQ itp.) To zostanie odblokowany.

Jednakże, jeśli rozmówca korzysta GetEnumerator() bezpośrednio (bardzo rzadko) i robi odczytać wszystkie dane i nie wywołanie Dispose(), to blokada nie zostanie zwolniony.

Musiałbym sprawdzić, czy finalizator jest gotowy; to może zostać wydany przez GC, ale nie chciałbym postawić na to pieniądze.