The Lazy<T>
type executes the value factory just once and
the value factory inherits the context of the first one to request the
Lazy<T>.Value
property's value. This can lead to deadlocks when
the value factory attempts to switch to the main thread.
When T
is Task<T2>
(because the value factory is an async method),
if the first caller had no access to the main thread, and the value factory
requires it, it will block. If later a second caller calls the Value
property
and that second caller is blocking the UI thread for its result, it will deadlock.
var lazy = new Lazy<Task<int>>(async delegate // analyzer flags this line
{
await Task.Yield();
return 3;
});
int value = await lazy.Value;
When the value factory passed to the Lazy<T>
constructor calls synchronously
blocking methods such as JoinableTaskFactory.Run
, only the first caller
can help any required transition to the main thread.
var lazy = new Lazy<int>(delegate
{
return joinableTaskFactory.Run(async delegate { // analyzer flags this line
int result = await SomeAsyncMethod();
return result + 3;
});
});
int value = lazy.Value;
Use AsyncLazy<T>
with an async value factory:
var lazy = new AsyncLazy<int>(async delegate
{
await Task.Yield();
return 3;
});
int value = await lazy.GetValueAsync();