Sie können in Ihrem Gebrauch des unveränderlichen Wörterbuchs absolut Thread-sicher sein. Die Datenstruktur selbst ist absolut threadsicher, aber Sie müssen Änderungen in einer Umgebung mit mehreren Threads sorgfältig vornehmen, um Datenverluste in Ihrem eigenen Code zu vermeiden.
Hier ist ein Muster, das ich häufig für solch ein Szenario verwende. Es erfordert keine Sperren, da die einzige Mutation, die wir tun, eine einzelne Speicherzuweisung ist. Wenn Sie mehrere Felder festlegen müssen, müssen Sie eine Sperre verwenden.
using System.Threading;
public class Something {
private ImmutableDictionary<string, string> dict = ImmutableDictionary<string, string>.Empty;
public void Add(string key, string value) {
// It is important that the contents of this loop have no side-effects
// since they can be repeated when a race condition is detected.
do {
var original = _dict;
if (local.ContainsKey(key)) {
return;
}
var changed = original.Add(key,value);
// The while loop condition will try assigning the changed dictionary
// back to the field. If it hasn't changed by another thread in the
// meantime, we assign the field and break out of the loop. But if another
// thread won the race (by changing the field while we were in an
// iteration of this loop), we'll loop and try again.
} while (Interlocked.CompareExchange(ref this.dict, changed, original) != original);
}
}
In der Tat, ich benutze dieses Muster so oft habe ich eine statische Methode für diesen Zweck definiert:
/// <summary>
/// Optimistically performs some value transformation based on some field and tries to apply it back to the field,
/// retrying as many times as necessary until no other thread is manipulating the same field.
/// </summary>
/// <typeparam name="T">The type of data.</typeparam>
/// <param name="hotLocation">The field that may be manipulated by multiple threads.</param>
/// <param name="applyChange">A function that receives the unchanged value and returns the changed value.</param>
public static bool ApplyChangeOptimistically<T>(ref T hotLocation, Func<T, T> applyChange) where T : class
{
Requires.NotNull(applyChange, "applyChange");
bool successful;
do
{
Thread.MemoryBarrier();
T oldValue = hotLocation;
T newValue = applyChange(oldValue);
if (Object.ReferenceEquals(oldValue, newValue))
{
// No change was actually required.
return false;
}
T actualOldValue = Interlocked.CompareExchange<T>(ref hotLocation, newValue, oldValue);
successful = Object.ReferenceEquals(oldValue, actualOldValue);
}
while (!successful);
Thread.MemoryBarrier();
return true;
}
Ihre Add-Methode dann viel einfacher wird:
public class Something {
private ImmutableDictionary<string, string> dict = ImmutableDictionary<string, string>.Empty;
public void Add(string key, string value) {
ApplyChangeOptimistically(
ref this.dict,
d => d.ContainsKey(key) ? d : d.Add(key, value));
}
}
Ja, Ich glaube, es ist nicht threadsicher in der Art, wie du es beschreibst. Sie müssen möglicherweise Ihre eigenen sperren um es herum. –
In welchem Szenario versuchen mehrere Threads, denselben Artikel einzufügen? Wenn Sie Ihre Arbeit parallelisieren, müssen Sie Ihre Daten über Threads/Maschinen partitionieren. –
Wenn die Zuweisung war '_dict [Schlüssel] = Wert;' - vielleicht sogar die 'ContainsKey'-Prüfung entfernen - das wäre threadsicher (?) –