Bạn hoàn toàn có thể an toàn chỉ trong việc sử dụng từ điển không thay đổi. Bản thân cấu trúc dữ liệu hoàn toàn an toàn với luồng, nhưng bạn áp dụng các thay đổi cho nó trong một môi trường đa luồng phải được viết cẩn thận để tránh mất dữ liệu trong mã của riêng bạn.
Đây là mẫu tôi thường xuyên sử dụng cho một trường hợp như vậy. Nó không yêu cầu khóa, vì đột biến duy nhất chúng tôi làm là gán một bộ nhớ duy nhất. Nếu bạn phải đặt nhiều trường, bạn cần sử dụng khóa.
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);
}
}
Trong thực tế, tôi sử dụng mô hình này để thường xuyên tôi đã xác định một phương pháp tĩnh cho mục đích này:
/// <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;
}
phương thức Add của bạn sau đó được đơn giản hơn nhiều:
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));
}
}
Vâng, Tôi tin rằng đó không phải là chủ đề an toàn theo cách bạn mô tả. Bạn có thể cần phải khóa riêng của bạn xung quanh nó. –
Kịch bản mà bạn có nhiều chủ đề cố gắng chèn cùng một mục là gì? Nếu bạn đang đi để song song công việc của bạn, bạn sẽ cần phải phân vùng dữ liệu của bạn trên các chủ đề/máy. –
Nếu nhiệm vụ là '_dict [key] = value;' - thậm chí có thể loại bỏ kiểm tra 'ContainsKey' - đó có thể là luồng an toàn (?) –