I saw this example at the end of Stephen\'s book.
This code can be accessed by more than one thread.
static int _simpleValue;
static readonly La
what is the problem with "different thread types that may call Value" in the first code?
There in nothing wrong with that code. But, imagine you had some CPU bound work along with the async initialization call. Picture it like this for example:
static readonly Lazy> MySharedAsyncInteger = new Lazy>(
async () =>
{
int i = 0;
while (i < 5)
{
Thread.Sleep(500);
i++;
}
await Task.Delay(TimeSpan.FromSeconds(2));
return 0;
});
Now, you aren't "guarded" against these kind of operations. I'm assuming Stephan mentioned the UI thread because you shouldn't be doing any operation that's longer than 50ms on it. You don't want your UI thread to freeze, ever.
When you use Task.Run to invoke the delegate, you're covering yourself from places where one might pass a long running delegate to your Lazy.
Stephan Toub talks about this in AsyncLazy:
Here we have a new
AsyncLazythat derives fromLazyand provides two constructors. Each of the constructors takes a function from the caller, just as does> Lazy. The first constructor, in fact, takes the same Func thatLazy. Instead of passing thatFuncdirectly down to the base constructor, however, we instead pass down a newFuncwhich simply uses StartNew to run the user-provided> Func. The second constructor is a bit more fancy. Rather than taking aFunc, it takes aFunc. With this function, we have two good options for how to deal with it. The first is simply to pass the function straight down to the base constructor, e.g:>
public AsyncLazy(Func> taskFactory) : base(taskFactory) { }
That option works, but it means that when a user accesses the Value property of this instance, the taskFactory delegate will be invoked synchronously. That could be perfectly reasonable if the
taskFactorydelegate does very little work before returning the task instance. If, however, thetaskFactorydelegate does any non-negligable work, a call to Value would block until the call totaskFactorycompletes. To cover that case, the second approach is to run thetaskFactoryusingTask.Factory.StartNew, i.e. to run the delegate itself asynchronously, just as with the first constructor, even though this delegate already returns aTask.