W przypadkach, gdy odczyty znacznie przewyższają liczbę zapisów lub (jakkolwiek częste) zapisy nie są współbieżne , odpowiednie może być podejście kopiowania przy zapisie .
Implementacja pokazana poniżej to
- bez zamka
- błyskawicznie szybki dla równoczesnych odczytów , nawet podczas jednoczesnych modyfikacji - bez względu na to, jak długo to zajmie
- ponieważ „migawki” są niezmienne, atomowość bez blokady jest możliwa, tzn.
var snap = _list; snap[snap.Count - 1];
nigdy (cóż, z wyjątkiem pustej listy oczywiście) nie rzuca, a także dostajesz bezpieczne wątkowo wyliczanie z semantyką migawek za darmo .. jak KOCHAM niezmienność!
- zaimplementowane ogólnie , mające zastosowanie do dowolnej struktury danych i każdego rodzaju modyfikacji
- bardzo prosty , tzn. łatwy do przetestowania, debugowania, weryfikacji poprzez odczytanie kodu
- do użytku w .Net 3.5
Aby kopiowanie przy zapisie działało, musisz utrzymywać swoje struktury danych w sposób niezmienny , tzn. Nikt nie może ich zmieniać po udostępnieniu ich innym wątkom. Kiedy chcesz zmodyfikować, ty
- sklonuj strukturę
- wprowadź zmiany w klonie
- zamień atomowo w odniesieniu do zmodyfikowanego klonu
Kod
static class CopyOnWriteSwapper
{
public static void Swap<T>(ref T obj, Func<T, T> cloner, Action<T> op)
where T : class
{
while (true)
{
var objBefore = Volatile.Read(ref obj);
var newObj = cloner(objBefore);
op(newObj);
if (Interlocked.CompareExchange(ref obj, newObj, objBefore) == objBefore)
return;
}
}
}
Stosowanie
CopyOnWriteSwapper.Swap(ref _myList,
orig => new List<string>(orig),
clone => clone.Add("asdf"));
Jeśli potrzebujesz większej wydajności, pomoże to uogólnić metodę, np. Utwórz jedną metodę dla każdego rodzaju modyfikacji (dodaj, usuń, ...), którą chcesz, i zapisz wskaźniki funkcji cloner
i op
.
Uwaga nr 1 Twoim obowiązkiem jest upewnić się, że nikt nie modyfikuje (rzekomo) niezmiennej struktury danych. Nic nie możemy zrobić w ogólnej implementacji, aby temu zapobiec, ale specjalizując się w niej List<T>
, możesz uchronić się przed modyfikacją za pomocą List.AsReadOnly ()
NB # 2 Uważaj na wartości z listy. Powyższe podejście do kopiowania chroni tylko ich członkostwo na liście, ale jeśli nie wstawisz ciągów, ale niektóre inne zmienne obiekty, musisz zadbać o bezpieczeństwo wątków (np. Blokowanie). Jest to jednak ortogonalne w stosunku do tego rozwiązania i np. Można zablokować zmienne wartości bez problemu. Musisz tylko być tego świadomy.
Uwaga nr 3 Jeśli struktura danych jest ogromna i często ją modyfikujesz, podejście kopiowania wszystkich danych w trakcie zapisu może być wygórowane zarówno pod względem zużycia pamięci, jak i kosztów procesora związanych z kopiowaniem. W takim przypadku możesz zamiast tego skorzystać z niezmiennych kolekcji MS .