In Fällen, in denen Lesevorgänge die Anzahl der Schreibvorgänge erheblich übersteigen oder (wie häufig auch immer) Schreibvorgänge nicht gleichzeitig ausgeführt werden , kann ein Copy-on-Write- Ansatz angebracht sein.
Die unten gezeigte Implementierung ist
- schlosslos
- Sehr schnell für gleichzeitige Lesevorgänge , auch wenn gleichzeitig Änderungen vorgenommen werden - egal wie lange sie dauern
- Da "Schnappschüsse" unveränderlich sind, ist eine sperrenlose Atomizität möglich, dh sie
var snap = _list; snap[snap.Count - 1];
wird niemals ( naja , abgesehen von einer leeren Liste natürlich) geworfen, und Sie erhalten außerdem kostenlos eine thread-sichere Aufzählung mit Schnappschuss-Semantik. Wie ich Unveränderlichkeit LIEBE!
- generisch implementiert , anwendbar auf jede Datenstruktur und jede Art von Modifikation
- ganz einfach , dh einfach zu testen, zu debuggen, zu überprüfen, indem Sie den Code lesen
- verwendbar in .Net 3.5
Damit Copy-on-Write funktioniert, müssen Sie Ihre Datenstrukturen effektiv unveränderlich halten , dh niemand darf sie ändern, nachdem Sie sie anderen Threads zur Verfügung gestellt haben. Wenn Sie ändern möchten, Sie
- Klonen Sie die Struktur
- Nehmen Sie Änderungen am Klon vor
- tauschen Sie den Verweis auf den modifizierten Klon atomar aus
Code
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;
}
}
}
Verwendung
CopyOnWriteSwapper.Swap(ref _myList,
orig => new List<string>(orig),
clone => clone.Add("asdf"));
Wenn Sie mehr Leistung benötigen, wird es helfen , die Methode ungenerify, zum Beispiel ein Verfahren für jede Art von Modifikation erstellen (Hinzufügen, Entfernen, ...) Sie wollen, und harte Code , um die Funktionszeiger cloner
und op
.
NB # 1 Es liegt in Ihrer Verantwortung sicherzustellen, dass niemand die (angeblich) unveränderliche Datenstruktur ändert. In einer generischen Implementierung können wir nichts tun, um dies zu verhindern. Wenn List<T>
Sie sich jedoch darauf spezialisieren, können Sie sich mit List.AsReadOnly () vor Änderungen schützen.
NB # 2 Achten Sie auf die Werte in der Liste. Der obige Ansatz zum Kopieren beim Schreiben schützt nur die Listenmitgliedschaft. Wenn Sie jedoch keine Zeichenfolgen, sondern einige andere veränderbare Objekte einfügen möchten, müssen Sie sich um die Thread-Sicherheit kümmern (z. B. Sperren). Dies ist jedoch orthogonal zu dieser Lösung, und z. B. kann das Sperren der veränderlichen Werte problemlos ohne Probleme verwendet werden. Sie müssen sich nur dessen bewusst sein.
Hinweis Nr. 3 Wenn Ihre Datenstruktur sehr umfangreich ist und Sie sie häufig ändern, ist der Ansatz des Kopierens beim Schreiben möglicherweise sowohl hinsichtlich des Speicherverbrauchs als auch der CPU-Kosten für das Kopieren unerschwinglich. In diesem Fall möchten Sie möglicherweise stattdessen die unveränderlichen Sammlungen von MS verwenden.