2015-01-21 5 views
5

Ich versuche, meine Cache aufzufüllen asynchronAsync Anruf innerhalb synchroner Funktion

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) {....} 

aber das gibt mir den Fehler:

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'.

Wie ich verstehe dies, weil GetOrAdd() nicht asynchron ist. Wie kann ich das Problem beheben?

Update: LazyAsync vorgeschlagen in den Kommentaren wird in meinem triviales Beispiel arbeiten. Oder Abhilfe wie folgt aus (kann auf jeden Fall mit einem gewissen Aufwand leben es führt):

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

Die Frage ist dann hat Microsoft haben einfach keine Zeit, um alle Schnittstellen zu aktualisieren unterstützen async oder ich versuche, etwas zutiefst falsch zu tun (und ConcurrentDictionary sollte nicht GetOrAddAsync() haben)?

+2

keine Antwort, aber vielleicht interessant für Sie: haben Sie [AsyncLazy] (http://blogs.msdn.com/b/pfxteam/archive/2011/01/15/10116210.aspx) gesehen? – Default

+1

Nun, Sie können es nicht mit der Schnittstelle, die Sie haben. 'await' funktioniert nur, wenn es sich um asynchrone Ebenen handelt, und Ihnen fehlt eine - die Methode' GetOrAdd' selbst. Sie würden eine Version benötigen, die selbst asynchron ist und auf die gesamte "GetOrAdd" -Methode wartet (beachten Sie, dass Sie das gerade nicht tun - das ist ein anderes Problem). Leider bedeutet dies, dass Sie Ihr eigenes ConcurrentDictionary schreiben müssen, was sehr schaden wird. – Luaan

+0

Warum sollte 'ConcurrentDictionary'' GetOrAddAsync' haben? Es ist eine synchrone Speichererfassung. Es gibt keinen Grund dafür, eine solche Methode zu haben. –

Antwort

10

Async-Methoden (oder Lambda) nur void oder Task oder Task<T> zurück, aber Ihr Lambda liefert string[] und somit Compiler verhindert, dass Sie.

await Das Schlüsselwort ist so optimiert, dass es synchron weiterläuft, wenn die Aufgabe bereits abgeschlossen ist. Eine Option besteht also darin, die Aufgabe selbst im Wörterbuch zu speichern und sich nicht darum zu kümmern, die abgeschlossene Aufgabe immer wieder abzuwarten.

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

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

Und wenn Sie

var item = await GetStuffAsync(...); 

erstes Mal tun es (a) warten, bis die Cache gespeicherten Task---Es beendet, nachdem es synchron fortsetzen wird.

Sie müssen darüber nachdenken, was passiert, wenn LoadAsync fehlschlägt. Weil wir die von LoadAsync zurückgegebene Aufgabe zwischenspeichern; Wenn das fehlschlägt, werden wir die fehlgeschlagene Aufgabe dummerweise zwischenspeichern. Möglicherweise müssen Sie damit umgehen.

+0

Es könnte tatsächlich möglich sein, diesen Ansatz zu verwenden, um einen Wrapper um 'ConcurrentDictionary' zu erstellen, der die asynchrone' GetOrAddAsync'-Methode verfügbar macht und die Fehler in sich selbst behandelt. Es wird immer noch eine Menge kniffliger Arbeit mit manuellen Sperren sein, so dass es immer noch eine bessere Idee sein könnte, den Zugriff mit einem der älteren Synchronisationsmuster zu synchronisieren. – Luaan

+1

anstatt 'async (x) schreiben => erwarten LoadAsync (x)' Sie können einfach 'x => LoadAsync (x)' schreiben. Sie sind die gleiche Methode. – Servy

+0

@Servy Ja, das sieht für mich auch überflüssig aus. Vielen Dank. Ich werde meine Antwort aktualisieren. –