Chcę implementacji List<T>
jako właściwości, której można bez wątpienia bezpiecznie używać.
Coś takiego:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Wygląda na to, że nadal muszę zwrócić kopię (sklonowaną) kolekcji, więc jeśli gdzieś iterujemy kolekcję i w tym samym czasie kolekcja jest ustawiona, nie jest zgłaszany żaden wyjątek.
Jak zaimplementować właściwość kolekcji bezpieczną dla wątków?
c#
collections
properties
thread-safety
Xaqron
źródło
źródło
IList<T>
(vsList<T>
)?List<T>
wdraża? Jeśli tak, czy możesz podać interfejs, którego potrzebujesz, zamiast pytać o wszystko, coList<T>
już masz?Odpowiedzi:
Jeśli celujesz w .Net 4, w przestrzeni nazw System.Collections.Concurrent jest kilka opcji
Możesz użyć
ConcurrentBag<T>
w tym przypadku zamiastList<T>
źródło
ConcurrentBag
jest to zbiór nieuporządkowany, więc w przeciwieństwie doList<T>
niego nie gwarantuje zamówienia. Nie możesz również uzyskać dostępu do elementów według indeksu.Mimo że uzyskał najwięcej głosów, zwykle nie można traktować
System.Collections.Concurrent.ConcurrentBag<T>
jako bezpiecznego zamiennika tego,System.Collections.Generic.List<T>
co jest (zwrócił na to już Radek Stromský), którego nie zamówiono.Ale istnieje klasa o nazwie,
System.Collections.Generic.SynchronizedCollection<T>
która istnieje już od czasu części .NET 3.0 frameworka, ale jest tak dobrze ukryta w miejscu, w którym nie można się tego spodziewać, że jest mało znana i prawdopodobnie nigdy się o nią nie natknąłeś (przynajmniej Nigdy nie zrobiłem).SynchronizedCollection<T>
jest kompilowany do zestawu System.ServiceModel.dll (który jest częścią profilu klienta, ale nie jest częścią przenośnej biblioteki klas).Mam nadzieję, że to pomoże.
źródło
Myślę, że stworzenie przykładowej klasy ThreadSafeList byłoby łatwe:
Po prostu klonujesz listę przed zażądaniem modułu wyliczającego, a zatem każde wyliczenie działa na kopii, której nie można modyfikować podczas działania.
źródło
T
jest to typ referencyjny, czy to nie zwróci po prostu nowej listy zawierającej odniesienia do wszystkich oryginalnych obiektów? Jeśli tak jest, to podejście może nadal powodować problemy z wątkami, ponieważ obiekty listy mogą być dostępne przez wiele wątków za pośrednictwem różnych „kopii” listy.newList
nie ma żadnych dodanych ani usuniętych elementów, które unieważniłyby moduł wyliczający).Nawet akceptowana odpowiedź to ConcurrentBag, nie sądzę, że jest to realne zastąpienie listy we wszystkich przypadkach, ponieważ komentarz Radka do odpowiedzi mówi: „ConcurrentBag jest kolekcją nieuporządkowaną, więc w przeciwieństwie do List nie gwarantuje zamówienia. Nie można również uzyskać dostępu do pozycji według indeksu ”.
Więc jeśli używasz .NET 4.0 lub nowszego, obejściem może być użycie ConcurrentDictionary z liczbą całkowitą TKey jako indeksem tablicy i TValue jako wartością tablicy. Jest to zalecany sposób zastępowania listy w kursie kolekcji współbieżnych języka C # Pluralsight . ConcurrentDictionary rozwiązuje oba wspomniane powyżej problemy: dostęp do indeksu i porządkowanie (nie możemy polegać na porządkowaniu, ponieważ jest to tablica mieszająca pod maską, ale obecna implementacja .NET oszczędza kolejność dodawania elementów).
źródło
ConcurrentDictionary
jest słownikiem, a nie listą. 2) Nie ma gwarancji zachowania porządku, jak stwierdza Twoja własna odpowiedź, co jest sprzeczne z podanym przez Ciebie powodem opublikowania odpowiedzi. 3) Odsyła do filmu bez umieszczania odpowiednich cytatów w tej odpowiedzi (co i tak może nie być zgodne z licencją).current implementation
jeśli nie jest to wyraźnie zagwarantowane w dokumentacji. Wdrożenie może ulec zmianie w dowolnym czasie bez powiadomienia.ArrayList
Klasa C # maSynchronized
metodę.Zwraca to bezpieczne opakowanie dla każdego wystąpienia
IList
. Wszystkie operacje muszą być wykonywane przez owijkę, aby zapewnić bezpieczeństwo nici.źródło
Jeśli spojrzysz na kod źródłowy listy T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), zauważysz, że jest tam klasa (co oczywiście jest wewnętrzny - dlaczego Microsoft, dlaczego?!?!) o nazwie SynchronizedList of T. Kopiuję wklejając kod tutaj:
Osobiście uważam, że wiedzieli, że można stworzyć lepszą implementację przy użyciu SemaphoreSlim , ale nie do końca do tego doszli.
źródło
_root
) w każdym dostępie (odczyt / zapis) sprawia, że jest to powolne rozwiązanie. Może lepiej, żeby ta klasa pozostała wewnętrzna.Clear()
po innych wywołaniach,this[index]
ale przed aktywacją blokady.index
nie jest już bezpieczny w użyciu i zgłosi wyjątek, gdy zostanie ostatecznie wykonany.Możesz także użyć bardziej prymitywnego
która używa blokady (zobacz ten post C # Blokowanie obiektu, który jest ponownie przypisywany w bloku blokady ).
Jeśli spodziewasz się wyjątków w kodzie, nie jest to bezpieczne, ale pozwala zrobić coś takiego:
Jedną z fajnych rzeczy jest to, że otrzymasz blokadę na czas trwania serii operacji (zamiast blokowania w każdej operacji). Co oznacza, że dane wyjściowe powinny wychodzić w odpowiednich fragmentach (moje użycie tego polegało na wyświetlaniu na ekranie niektórych danych wyjściowych z zewnętrznego procesu)
Bardzo podoba mi się prostota + przejrzystość ThreadSafeList +, która odgrywa ważną rolę w zatrzymywaniu awarii
źródło
W .NET Core (w dowolnej wersji) można użyć ImmutableList , który ma wszystkie funkcje
List<T>
.źródło
Wierzę,
_list.ToList()
że zrobię ci kopię. Możesz również zapytać o to, jeśli potrzebujesz, na przykład:W każdym razie msdn twierdzi, że to rzeczywiście kopia, a nie tylko odniesienie. Aha, i tak, będziesz musiał zablokować ustaloną metodę, jak wskazali inni.
źródło
Wygląda na to, że wiele osób, które to znalazły, chce mieć kolekcję o dynamicznych rozmiarach, bezpieczną dla wątków i indeksowaną. Najbliższa i najłatwiejsza rzecz, jaką znam, byłaby.
Wymagałoby to upewnienia się, że klucz jest odpowiednio obciążony, jeśli chcesz normalnego zachowania podczas indeksowania. Jeśli zachowasz ostrożność, .count może wystarczyć jako klucz dla każdej dodawanej nowej pary klucz-wartość.
źródło
Sugerowałbym każdemu, kto ma do czynienia ze
List<T>
scenariuszami wielowątkowymi, aby przyjrzał się niezmiennym kolekcjom, w szczególności ImmutableArray .Uważam, że jest to bardzo przydatne, gdy masz:
Może być również przydatny, gdy musisz zaimplementować zachowanie podobne do transakcji (tj. Cofnąć operację wstawiania / aktualizowania / usuwania w przypadku niepowodzenia)
źródło
Oto klasa, o którą prosiłeś:
źródło
this.GetEnumerator();
kiedy @Tejs sugerujethis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?Zasadniczo, jeśli chcesz bezpiecznie wyliczać, musisz użyć blokady.
Więcej informacji można znaleźć w witrynie MSDN. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Oto część MSDN, która może Cię zainteresować:
Publiczne statyczne elementy członkowskie tego typu (udostępnione w języku Visual Basic) są bezpieczne wątkowo. Nie ma gwarancji, że żadne elementy członkowskie instancji będą bezpieczne dla wątków.
Lista może obsługiwać wielu czytelników jednocześnie, o ile kolekcja nie jest modyfikowana. Wyliczanie w kolekcji nie jest wewnętrznie procedurą bezpieczną dla wątków. W rzadkich przypadkach, gdy wyliczenie rywalizuje z co najmniej jednym dostępem do zapisu, jedynym sposobem zapewnienia bezpieczeństwa wątków jest zablokowanie kolekcji podczas całego wyliczania. Aby umożliwić dostęp do kolekcji przez wiele wątków w celu odczytu i zapisu, należy zaimplementować własną synchronizację.
źródło
Oto klasa listy bezpiecznych wątków bez blokady
źródło
Użyj do tego
lock
instrukcji. ( Przeczytaj tutaj, aby uzyskać więcej informacji. )FYI, prawdopodobnie nie jest to dokładnie to, o co prosisz - prawdopodobnie chcesz zablokować dalej w swoim kodzie, ale nie mogę tego założyć. Przyjrzyj się
lock
słowu kluczowemu i dostosuj jego użycie do swojej konkretnej sytuacji.Jeśli trzeba, można
lock
w obuget
iset
bloku przy użyciu_list
zmiennej, która stałaby się więc odczytu / zapisu nie mogą występować jednocześnie.źródło
lock
stwierdzenia. Korzystając z podobnego przykładu, mógłbym argumentować, że nie powinniśmy używać pętli for, ponieważ można zablokować aplikację bez żadnego wysiłku:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
ilock (typeof(this))
są duże, nie, nie.