Sto tentando di convertire il metodo seguente (esempio semplificato) di essere asincrona, come la chiamata cacheMissResolver
può essere costoso in termini di tempo (ricerca nel database, chiamata di rete):Modo corretto per convertire il metodo in asincrono in C#?
// 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;
}
}
Ci sono un sacco di materiali on-line sul consumo di metodi asincroni, ma il consiglio che ho trovato nel produrli sembra meno chiaro. Dato che questo è destinato a far parte di una biblioteca, uno dei miei tentativi è corretto?
// 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;
}
}
Sta usando SemaphoreSlim
il modo corretto per sostituire una dichiarazione di blocco in un metodo asincrono? (Non posso attendere nel corpo di una dichiarazione di blocco.)
Devo inserire l'argomento cacheMissResolver
di tipo Func<Task<Thing>>
? Anche se ciò pone l'onere di assicurarsi che la funzione resolver sia asincrona sul chiamante (eseguendo il wrapping in Task.Run
, so che verrà scaricato su un thread in background se impiega molto tempo).
Grazie.
Considerare l'utilizzo di ['AsyncLock'] (https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock). –
Che cos'è 'cache'? – dbc
@Timothy Shields - Sembra utile! Grazie. – rob