2012-06-19 9 views
19

Zastanawiam się, czy metody rozszerzenia Linq są atomowe? Czy potrzebuję do lock dowolnego obiektu IEnumerable używanego w wątkach przed jakąkolwiek iteracją?Czy wielokrotne metody Linq są wątkowe?

Czy zadeklarowanie zmiennej jako volatile ma na to jakiś wpływ?

Podsumowując, która z poniższych jest najlepsza, bezpieczne dla wątków, operacja?

1- Bez zamków:

IEnumerable<T> _objs = //... 
var foo = _objs.FirstOrDefault(t => // some condition 

2- tym oświadczenia blokady:

IEnumerable<T> _objs = //... 
lock(_objs) 
{ 
    var foo = _objs.FirstOrDefault(t => // some condition 
} 

3- zgłaszania zmiennych jak lotne:

volatile IEnumerable<T> _objs = //... 
var foo = _objs.FirstOrDefault(t => // some condition 
+0

Nie są bezpieczne dla wątków. Zobacz http://stackoverflow.com/questions/9995266/how-to-create-a-thread-safe-generic-list – stuartd

Odpowiedz

20

Interfejs IEnumerable<T> nie jest bezpieczny dla wątków. Zobacz dokumentację na stronie http://msdn.microsoft.com/en-us/library/s793z9y2.aspx, która zawiera:

Moduł wyliczający pozostaje ważny, o ile kolekcja pozostaje niezmieniona. W przypadku wprowadzenia zmian w kolekcji, takich jak dodawanie, modyfikowanie lub usuwanie elementów, moduł wyliczający jest nieodwracalnie unieważniany, a jego zachowanie jest niezdefiniowane.

Moduł wyliczający nie ma wyłącznego dostępu do kolekcji; dlatego wyliczanie poprzez kolekcję nie jest samoistnie procedurą bezpieczną dla wątków. Aby zagwarantować bezpieczeństwo wątków podczas wyliczania, możesz zablokować kolekcję podczas całego wyliczania. Aby umożliwić dostęp do kolekcji przez wiele wątków do czytania i pisania, musisz zaimplementować własną synchronizację.

Linq nie zmienia żadnego z powyższych.

Blokowanie może być oczywiście używane do synchronizowania dostępu do obiektów. Musisz zablokować obiekt wszędzie tam, gdzie masz do niego dostęp, a nie tylko podczas iteracji.

Zgłoszenie kolekcji jako lotnej nie będzie miało pozytywnego wpływu. Daje to tylko barierę pamięci przed odczytaniem i po zapisaniu odniesienia do kolekcji. Nie synchronizuje odczytu lub zapisu kolekcji.

7

W skrócie, nie są one bezpieczne dla wątków, jak wspomniano powyżej.

Nie oznacza to jednak, że musisz zablokować przed "każdą iteracją".

Należy zsynchronizować wszystkie operacje zmieniające kolekcję (dodawać, modyfikować lub usuwać elementy) z innymi operacjami, które (dodawanie, modyfikowanie, usuwanie elementów lub odczytywanie elementów).

Jeśli jednocześnie wykonujesz operacje odczytu w zbiorze, nie jest potrzebne blokowanie. (w takim przypadku uruchamianie komend LINQ, takich jak Średni, Zawiera, ElementAtOrDomyślnie wszystko byłoby w porządku)

Jeśli elementy w zbiorze mają długość słowa maszynowego, na przykład Int na większości komputerów 32-bitowych, to zmiana wartości tego elementu jest już wykonywany atomowo.W takim przypadku nie dodawaj ani nie usuwaj elementów z kolekcji bez blokowania, ale modyfikowanie wartości może być w porządku, jeśli możesz uporać się z pewnym niedeterminizmem w swoim projekcie.

Na koniec można rozważyć precyzyjne blokowanie poszczególnych elementów lub sekcji kolekcji zamiast blokować całą kolekcję.

Powiązane problemy