Assume the following code
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
Lazy<TimeSpan> lm = new Lazy<TimeSpan>(GetDataAsync1, System.Threading.LazyThreadSafetyMode.PublicationOnly);
return new string[] { "value1", "value2", lm.Value.ToString() };
}
private TimeSpan GetDataAsync1()
{
return GetTS().ConfigureAwait(false).GetAwaiter().GetResult();
}
// I Cant change this method, and what is inside it...
private async Task<TimeSpan> GetTS()
{
var sw = Stopwatch.StartNew();
using (var client = new HttpClient())
{
var result = await client.GetAsync("https://www.google.com/");
}
sw.Stop();
return sw.Elapsed;
}
}
The point is that I am getting some data from remote server, and want to cache that for later use. As remote server may fail in a given point, I dont want to cache exception, but only success result... So keeping than awaiting the value will not work for me
// Cant use this, because this caches failed exception as well
Lazy<Task...> lz = ...
await lz.Value
But above snipped, as expected produce a deadlock, given that I cant change GetTS, is it possible to force Lazy work with my logic?
The problem actually has nothing to do with Lazy<T>. The deadlock is because it's blocking on asynchronous code.
In this code:
private TimeSpan GetDataAsync1()
{
return GetTS().ConfigureAwait(false).GetAwaiter().GetResult();
}
the ConfigureAwait(false) does nothing. ConfigureAwait configures awaits, not tasks, and there is no await there.
The best option is to go async all the way. If exceptions are a concern, you can use AsyncLazy<T> and pass AsyncLazyFlags.RetryOnFailure.
If you can't go async all the way, the next best option would be to go synchronous all the way. If you can't do either of these, then you'll have to choose a sync-over-async hack; be aware that there is no hack that works in all situations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With