2013-06-25 28 views
7

ConcurrentDictionary Pitfall - Are delegates factories from GetOrAdd and AddOrUpdate synchronized? lưu ý rằng AddOrUpdate không phải là nguyên tử (và không thể đảm bảo đại biểu sẽ không chạy nhiều lần).addorupdate nguyên tử (cố gắng viết tên khóa bằng cách sử dụng từ điển đồng thời)

Tôi đang cố gắng để thực hiện một tên khóa thực hiện bằng cách sử dụng từ điển đồng thời a la here, nhưng mà từ điển không nên phát triển mãi mãi, như thế này:

public class ConcurrentDictionaryNamedLocker : INamedLocker 
{ 
    // the IntObject values serve as the locks and the counter for how many RunWithLock jobs 
    // are about to enter or have entered the critical section. 
    private readonly ConcurrentDictionary<string, IntObject> _lockDict = new ConcurrentDictionary<string, IntObject>(); 
    private static readonly IntObject One = new IntObject(1); 
    private readonly Func<string, IntObject, IntObject> _decrementFunc = (s, o) => o - 1; 
    private readonly Func<string, IntObject, IntObject> _incrementFunc = (s, o) => o + 1; 
    private readonly Func<string, IntObject> _oneFunc = s => new IntObject(1); 
    private readonly Func<string, IntObject> _zeroFunc = s => new IntObject(0); 

    public TResult RunWithLock<TResult>(string name, Func<TResult> body) 
    { 
     name = name.ToLower(); 
     TResult toReturn; 
     lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc)) 
     { 
      toReturn = body(); 
      if (!_lockDict.TryRemove(name, One)) 
       _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc); 
     } 
     return toReturn; 
    } 

    public void RunWithLock(string name, Action body) 
    { 
     name = name.ToLower(); 
     lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc)) 
     { 
      body(); 
      if (!_lockDict.TryRemove(name, One)) 
       _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc); 
     } 
    } 
} 

Nhưng vấn đề là AddOrUpdate không phải là nguyên tử , vì vậy tôi thấy rằng các mục nhập thường không bị xóa khi có tranh chấp. Tôi khá chắc chắn rằng mặc dù nếu AddOrUpdate là nguyên tử mã trên sẽ làm công việc của mình và các mục sẽ được gỡ bỏ một cách thích hợp.

Lưu ý việc sử dụng loại bỏ điều kiện bằng phương pháp mở rộng khóa + val TryRemove (khóa, val) được đề cập here. Ngoài ra, IntObject là một trình bao bọc đối tượng có thể thay đổi đơn giản của một int.

Tùy chọn của tôi là gì? Có bất kỳ triển khai từ điển đồng thời có 1. loại bỏ điều kiện nguyên tử (trên khóa và giá trị) và 2. AddOrUpdate là nguyên tử và đảm bảo các đại biểu không được chạy nhiều hơn một lần?

Bạn có ý tưởng nào khác không? Tôi muốn tủ khóa được đặt tên là nhanh nhưng không có vấn đề về áp lực bộ nhớ được cung cấp cho một không gian tên khóa không giới hạn nhưng không có nhiều tranh cãi về một tên đã cho. Theo như tôi biết, chuỗi ký tự khóa theo tên phát triển mãi mãi và không bao giờ bị làm sạch và có các tác dụng phụ khác. Và mutexes là bán chậm và có nhiều phiền toái (260 char giới hạn).

+0

Bạn có thể sử dụng một từ điển được gõ là 'ConcurrentDictionary >' như tôi đã mô tả trong câu trả lời này http://stackoverflow.com/a/12611341/1236734 –

Trả lời

1

Nó sẽ giúp xem mã chính xác cho IntValue vì đó là mã đang chạy trong đại biểu AddOrUpdate.

Tôi nghĩ vấn đề là mã hy vọng IntValue trường hợp được cả hai:

  • Mutable để khóa một IntValue trường hợp duy nhất liên quan đến mỗi chuỗi (mặc dù số lượng tài liệu tham khảo incrementing sau)
  • Immutable để so sánh IntValue s đến một tĩnh One công trình là tiêu chí loại bỏ

Nếu tôi thay đổi mã để IntValue hỗ trợ một không bất biến này dường như làm việc:

private readonly Func<string, IntObject> _zeroFunc = s => IntObject.Zero; 

...

public void RunWithLock(string name, Action body) 
{ 
    name = name.ToLower(); 
    lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc)) 
    { 
     body(); 
     _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc); 
     _lockDict.TryRemove(name, IntObject.Zero); 
    } 
} 

Nhưng tôi đã chọn để thực hiện các phương pháp IntValue như thế này và như không khai thác:

internal IntObject Dec(int p) 
    { 
     var newVal = Interlocked.Decrement(ref value); 
     if (newVal == 0) return Zero; 
     return this; 
    } 
Các vấn đề liên quan