2012-03-28 33 views
9

Tôi có một số trường hợp tôi sử dụng ConcurrentDictionary<TKey, TValue> để lưu vào bộ nhớ đệm các giá trị, nhưng thường tôi cần thực hiện xác thực giá trị để quyết định có thêm nó vào bộ nhớ cache bằng cách sử dụng ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey, Func<TKey, TValue>) hay không.Làm cách nào để tôi có thể nói 'ConcurrentDictionary.GetOrAdd` để không thêm giá trị?

thường dọc theo dòng:

private readonly ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>(); 
public ISomeObject CreateSomeObject(Type someType) 
{ 
    return someObjectCache.GetOrAdd(someType, type => 
    { 
     if(!Attribute.IsDefined(someType, typeof(SomeAttribute)) 
      // Do something here to avoid the instance from being added to 
      // `someObjectCache` 

     ISomeObject someObject; 
     // Typical factory functionality goes here 
     return someObject; 
    }); 
} 

Con đường tôi xử lý ngày hôm nay là để ném một ngoại lệ mà dường như làm việc tốt, nhưng tôi muốn một cách tiếp cận sạch (có thể là một lá cờ tôi có thể thiết lập hoặc một giá trị cụ thể tôi có thể thiết lập giá trị trả về) để hủy bỏ GetOrAdd từ bên trong lambda (mặc dù nó có thể được thay thế bằng phương pháp thổi hoàn toàn).

Dựa trên kinh nghiệm của tôi với các phương pháp LINQ khác, trả lại null sẽ dẫn đến giá trị được thêm mà không được kiểm tra như vậy (và đọc IL cho GetOrAdd có vẻ như nó sẽ dẫn đến cùng một vấn đề), vì vậy tôi đừng nghĩ rằng nó sẽ hoạt động.

Có cách nào tôi có thể tránh sử dụng ngoại lệ để hủy thêm bằng GetOrAdd không?

Trả lời

10

Từ nội dung tôi đã đọc, có no guarantee that the Add factory method will only be called a single time amongst all callers to Get for the same key.

Phần liên quan từ trang đó là ở phía dưới, được trích dẫn ở đây:

Ngoài ra, mặc dù tất cả các phương pháp ConcurrentDictionary (Tất TKey, TValue) là thread-an toàn, không phải tất cả các phương pháp là nguyên tử, đặc biệt GetOrAdd và AddOrUpdate. Người dùng ủy nhiệm được chuyển đến các phương thức này là được gọi bên ngoài khóa nội bộ của từ điển. (Điều này được thực hiện để ngăn chặn mã không rõ từ chặn tất cả các chủ đề.) Vì vậy nó là có thể cho chuỗi sự kiện này xảy ra:

1) threadA gọi GetOrAdd, thấy không có mục và tạo ra một mục mới vào Thêm bởi gọi đại biểu valueFactory.

2) threadB gọi GetOrAdd đồng thời, giá trị của nóGiao diện được ủy quyền là được gọi và nó đến khóa nội bộ trước threadA và vì vậy cặp khóa-giá trị mới được thêm vào từ điển.

3) đại biểu sử dụng threadA của hoàn tất, và sợi chỉ đến được khóa , nhưng bây giờ thấy rằng mục đã tồn tại

4) threadA thực hiện một "Nhận", và trả về dữ liệu mà trước đây thêm bởi threadB.

Do đó, không đảm bảo rằng dữ liệu được trả về bởi GetOrAdd là cùng một dữ liệu được tạo bởi giá trị của luồng. Một chuỗi sự kiện tương tự có thể xảy ra khi AddOrUpdate được gọi.

Cách tôi đọc điều này là ngay cả khi bạn đang gọi một số khóa trong Thêm đại biểu, bạn không được đảm bảo rằng giá trị trả về từ tiện ích của bạn là giá trị thực sự được sử dụng.

Vì vậy, bạn không cần phải thêm bất kỳ khóa hơn nữa, và thay vào đó có thể có thể sử dụng mẫu sau:

private ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>(); 
public ISomeObject CreateSomeObject(Type someType) 
{ 

    ISomeObject someObject; 
    if (someObjectCache.TryGet(someType, out someObject)) 
    { 
     return someObject; 
    } 

    if (Attribute.IsDefined(someType, typeof(SomeAttribute)) 
    { 
     // init someObject here 
     someObject = new SomeObject(); 

     return someObjectCache.GetOrAdd(someType, someObject); // If another thread got through here first, we'll return their object here. 
    } 

    // fallback functionality goes here if it doesn't have your attribute. 
} 

Vâng, điều này sẽ dẫn đến một số tiềm năng cho các đối tượng mới được tạo ra có khả năng nhiều lần , nhưng người gọi tất cả sẽ nhận được kết quả tương tự, ngay cả khi nhiều người được gọi. Giống như GetOrAdd hiện nay.

+0

Nó sẽ (và _I have_) nhưng một khi bạn lộn xộn với tất cả các cơ chế khóa chủ đề cần thiết, nó trở nên khá lộn xộn (không phải của tôi không yêu cầu khóa ... chỉ không đến mức tương tự). Tôi đang tìm kiếm một giải pháp sạch hơn, không phải là một giải pháp đòi hỏi phải có phiên bản 'ConcurrentDictionary' của riêng tôi một cách hiệu quả. –

+0

Cập nhật câu trả lời của tôi - GetOrAdd không khóa trên phần Thêm, chỉ xử lý các bit mảng nội bộ. Vì vậy, như tôi thấy nó, bạn không nên cần bất kỳ khóa ở đây nếu ý định của bạn là chỉ thêm xác nhận. –

+0

Có, tôi đã đọc phần tài liệu đó, nhưng đây vẫn không phải là những gì tôi đang tìm kiếm. Nếu tôi muốn viết rất nhiều mã thì tôi sẽ tự xử lý khóa. –

3

Tất cả các vấn đề trong khoa học máy tính có thể được giải quyết bằng cách khác mức gián tiếp

// the dictionary now stores functions 
private readonly ConcurrentDictionary<Type, Func<ISomeObject>> someObjectCache = 
    new ConcurrentDictionary<Type, Func<ISomeObject>>(); 

public ISomeObject CreateSomeObject(Type someType) { 
    return someObjectCache.GetOrAdd(someType, _ => { 
    if(ShouldCache(someType)) { 
     // caching should be used 
     // return a function that returns a cached instance 
     var someObject = Create(someType); 
     return() => someObject; 
    } 
    else { 
     // no caching should be used 
     // return a function that always creates a new instance 
     return() => Create(someType); 
    } 
    })(); // call the returned function 
} 

private bool ShouldCache(Type someType) { 
    return Attribute.IsDefined(someType, typeof(SomeAttribute)); 
} 

private ISomeObject Create(Type someType) { 
    // typical factory functionality ... 
} 

Bây giờ giá trị được lưu trữ trong từ điển là một chức năng; khi bạn không muốn bộ nhớ đệm xảy ra, hàm luôn tạo một cá thể mới; khi bạn muốn bộ nhớ đệm xuất hiện, hàm trả về một cá thể được lưu trong bộ nhớ cache.

+0

Bạn có chắc chắn điều này sẽ hoạt động? Tất cả những gì bạn đã làm ở đây là di chuyển dấu kiểm đến một hàm và trả về một thể hiện của kiểu đó. Một số giải thích thêm có lẽ là bắt buộc. –

+0

Chắc chắn, tôi sẽ thêm một số chi tiết .... –

+0

Di chuyển séc đến một chức năng riêng biệt không có liên quan gì đến trái tim của câu hỏi, nó chỉ là một số cấu trúc lại phổ biến .... –

Các vấn đề liên quan