2015-01-21 43 views
5

Tôi đang cố gắng để cư trú bộ nhớ cache của tôi không đồng bộasync cuộc gọi trong phạm vi chức năng đồng bộ

static ConcurrentDictionary<string, string[]> data = new ConcurrentDictionary<string, string[]>(); 

public static async Task<string[]> GetStuffAsync(string key) 
{ 
    return data.GetOrAdd(key, async (x) => { 
     return await LoadAsync(x); 
    }); 
} 

static async Task<string[]> LoadAsync(string key) {....} 

nhưng điều này mang lại cho tôi những lỗi:

Cannot convert async lambda expression to delegate type 'System.Func'.

An async lambda expression may return void, Task or Task, none of which are convertible to 'System.Func'.

Như tôi hiểu điều này là bởi vì GetOrAdd() không phải là không đồng bộ. Làm cách nào để khắc phục sự cố?

Cập nhật: LazyAsync gợi ý trong các ý kiến ​​sẽ làm việc trong ví dụ tầm thường của tôi. Hoặc, workaround như thế này (chắc chắn có thể sống chung với một số phí nó giới thiệu):

public static async Task<string[]> GetStuffAsync(string key) 
{ 
    string[] d = null; 
    if (!data.ContainsKey(key)) 
     d = await LoadAsync(key); 
    return data.GetOrAdd(key, d); 
} 

Câu hỏi đặt ra sau đó trở nên đã làm Microsoft chỉ không có thời gian để cập nhật tất cả các giao diện để hỗ trợ async hay tôi đang cố gắng để làm một cái gì đó sâu sắc sai (và ConcurrentDictionary không nên có GetOrAddAsync())?

+2

không phải là câu trả lời, nhưng có thể thú vị cho bạn: bạn đã thấy [AsyncLazy] (http://blogs.msdn.com/b/pfxteam/archive/2011/01/15/10116210.aspx) chưa? – Default

+1

Vâng, bạn không thể làm điều đó bằng cách sử dụng giao diện bạn có. 'await' chỉ hoạt động khi nó trên các lớp không đồng bộ, và bạn đang thiếu một - phương thức' GetOrAdd'. Bạn sẽ cần một phiên bản không đồng bộ, và chờ toàn bộ phương thức 'GetOrAdd' (lưu ý cách bạn không thực sự làm điều đó ngay bây giờ - đó là một vấn đề khác). Đáng buồn thay, điều này có nghĩa là viết ConcurrentDictionary của riêng bạn, điều này sẽ gây tổn thương. – Luaan

+0

Tại sao 'ConcurrentDictionary' có' GetOrAddAsync'? Nó là một bộ sưu tập bộ nhớ đồng bộ. Không có lý do gì để nó có một phương pháp như vậy. –

Trả lời

10

Phương thức không đồng bộ (hoặc lambda) chỉ có thể trả lại void hoặc Task hoặc Task<T> nhưng lambda của bạn trả lại string[] và do đó trình biên dịch ngăn bạn.

await từ khóa được tối ưu hóa để tiếp tục đồng bộ khi Tác vụ đã được hoàn thành. Vì vậy, một tùy chọn là để lưu trữ các nhiệm vụ chính nó trong từ điển và không lo lắng về việc chờ đợi nhiệm vụ hoàn thành một lần nữa và một lần nữa.

private static ConcurrentDictionary<string, Task<string[]>> data = 
    new ConcurrentDictionary<string, Task<string[]>>(); 

public static Task<string[]> GetStuffAsync(string key) 
{ 
    return data.GetOrAdd(key, LoadAsync); 
} 

Và khi bạn làm

var item = await GetStuffAsync(...); 

lần đầu tiên nó sẽ (a) chờ đến khi công tác lưu trữ kết thúc --There sau đó sẽ tiếp tục đồng bộ.

Bạn sẽ phải suy nghĩ về điều gì sẽ xảy ra khi LoadAsync không thành công. Bởi vì chúng tôi đang lưu vào bộ nhớ cache Tác vụ được trả về bởi LoadAsync; nếu điều đó không thành công, chúng tôi sẽ xóa nhầm nhiệm vụ không thành công. Bạn có thể cần phải xử lý việc này.

+0

Có thể thực sự có thể sử dụng phương pháp này để xây dựng trình bao bọc xung quanh 'ConcurrentDictionary' sẽ hiển thị phương thức' GetOrAddAsync' không đồng bộ và xử lý các lỗi bên trong chính nó. Nó vẫn sẽ là rất nhiều công việc phức tạp với các khóa thủ công, vì vậy nó vẫn có thể là một ý tưởng tốt hơn để đồng bộ hóa truy cập bằng cách sử dụng một trong các mẫu đồng bộ hóa cũ hơn. – Luaan

+1

thay vì viết 'async (x) => đang chờ LoadAsync (x) 'bạn chỉ có thể viết' x => LoadAsync (x) '. Họ là cùng một phương pháp. – Servy

+0

@Servy yes, trông cũng thừa đối với tôi. Cảm ơn. Tôi sẽ cập nhật câu trả lời của mình. –

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