Ich versuche, die folgende Methode (vereinfachtes Beispiel) zu konvertieren asynchron zu sein, da die cacheMissResolver
Aufruf in Bezug auf die Zeit (Datenbanksuche, Netzanruf) teuer sein kann:Korrekter Weg, Methode in C# in async zu konvertieren?
// Synchronous version
public class ThingCache
{
private static readonly object _lockObj;
// ... other stuff
public Thing Get(string key, Func<Thing> cacheMissResolver)
{
if (cache.Contains(key))
return cache[key];
Thing item;
lock(_lockObj)
{
if (cache.Contains(key))
return cache[key];
item = cacheMissResolver();
cache.Add(key, item);
}
return item;
}
}
Es gibt viele Materialien Online über asynchrone Methoden zu konsumieren, aber die Ratschläge, die ich bei der Herstellung gefunden habe, scheinen weniger klar zu sein. In Anbetracht dessen, dass dies Teil einer Bibliothek sein soll, ist einer meiner Versuche unten korrekt?
// Asynchronous attempts
public class ThingCache
{
private static readonly SemaphoreSlim _lockObj = new SemaphoreSlim(1);
// ... other stuff
// attempt #1
public async Task<Thing> Get(string key, Func<Thing> cacheMissResolver)
{
if (cache.Contains(key))
return await Task.FromResult(cache[key]);
Thing item;
await _lockObj.WaitAsync();
try
{
if (cache.Contains(key))
return await Task.FromResult(cache[key]);
item = await Task.Run(cacheMissResolver).ConfigureAwait(false);
_cache.Add(key, item);
}
finally
{
_lockObj.Release();
}
return item;
}
// attempt #2
public async Task<Thing> Get(string key, Func<Task<Thing>> cacheMissResolver)
{
if (cache.Contains(key))
return await Task.FromResult(cache[key]);
Thing item;
await _lockObj.WaitAsync();
try
{
if (cache.Contains(key))
return await Task.FromResult(cache[key]);
item = await cacheMissResolver().ConfigureAwait(false);
_cache.Add(key, item);
}
finally
{
_lockObj.Release();
}
return item;
}
}
Nutzt SemaphoreSlim
die richtige Art und Weise eine Sperre Anweisung in einem Asynchron-Verfahren zu ersetzen? (Ich kann im Körper einer Lock-Anweisung nicht erwarten.)
Sollte ich stattdessen das cacheMissResolver
Argument vom Typ Func<Task<Thing>>
machen? Obwohl dies die Last auf sich zieht sicherzustellen, dass die Resolver-Funktion asynchron für den Aufrufer ist (Wrapping in Task.Run
, weiß ich, dass es in einen Hintergrundthread ausgelagert wird, wenn es lange dauert).
Danke.
Verwenden Sie [AsyncLock'] (https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock). –
Was ist 'Cache'? – dbc
@Timothy Shields - Das sieht nützlich aus! Vielen Dank. – rob