2009-12-16 10 views
5

Dlaczego SynchronizedCollection<T> nie nabywa blokadę na SyncObj w wyraźnej realizacji IEnumerable.GetEnumerator()Dlaczego SynchronizedCollection <T> nie blokuje na IEnumerable.GetEnumerator()

IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.items.GetEnumerator(); 
    } 

niejawnego realizacji ma nabyć blokadę SyncOb (zweryfikowane przez reflektor).

Może to być problem podczas pętli foreach w tej kolekcji. Jeden wątek mógł uzyskać blokadę, a drugi mógł próbować odczytać go za pomocą foreach?

Odpowiedz

1

Modyfikowanie kolekcji podczas gdy ktoś używa iteratora jest mimo wszystko naruszeniem współbieżności.

Jaka byłaby Twoja alternatywa? Zablokować kolekcję podczas pobierania iteratora i nie odblokowywać go, dopóki iterator nie zostanie zniszczony?

1

Zamierzam powiedzieć, że może to być błąd (ed: lub przynajmniej niespójność) w implementacji. Reflektor pokazuje dokładnie to, co widzisz, że wszystkie inne jawne wywołania implementacji są blokowane pod danym SyncRoot, z wyjątkiem IEnumerable.GetEnumerator().

Być może należy przesłać zgłoszenie pod numer Microsoft Connect.

Wierzę powodem niejawna GetEnumerator() metoda nazywa lock dlatego List<T>.GetEnumerator() tworzy nowy Enumerator<T> która opiera się na polu prywatnym _version na liście. Chociaż zgadzam się z innymi plakatami, że nie widzę użycia w blokowaniu wywołania GetEnumerator(), ale ponieważ konstruktor Enumerator<T> opiera się na polach bez wątków, sensownym byłoby zablokowanie. Lub przynajmniej zachowaj spójność z niejawnymi implementacjami.

4

Ponieważ nie ma sposobu, aby klasa dowiedziała się, kiedy kod klienta jest wykonywany za pomocą iteratora. Jest to jeden z powodów, dla których biblioteka MSDN Library docs w klasach System.Collection zawsze ostrzega, że ​​iterowanie kolekcji nie jest bezpieczne dla wątków.

Chociaż zapomnieli o tym wspomnieć w artykule dla SynchronizedCollection. Ironia ...

+1

Myślę, że to, o czym on mówi, to fakt, że niejawna implementacja GetEnumerator() rzeczywiście wywołuje blokadę. – user7116

+0

Rzeczywiście tak. Głupie, nie ma niczego, co trzeba zablokować. Wewnętrzne odwołanie do listy nie może się zmienić, a jedynie zawartość. Iterator zapisuje tylko odwołanie do listy. –

+0

Wierzę, że powinno, ponieważ wywołanie 'GetEnumerator()' ostatecznie tworzy nowy 'Enumerator ', który opiera się na polu 'List ._version'. – user7116

Powiązane problemy