Lazy shared async resource — Clarification?

后端 未结 1 977
无人共我
无人共我 2021-02-04 17:58

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         


        
相关标签:
1条回答
  • 2021-02-04 18:18

    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<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(
    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<T>.

    Stephan Toub talks about this in AsyncLazy:

    Here we have a new AsyncLazy<T> that derives from Lazy<Task<T>> and provides two constructors. Each of the constructors takes a function from the caller, just as does Lazy<T>. The first constructor, in fact, takes the same Func that Lazy<T>. Instead of passing that Func<T> directly down to the base constructor, however, we instead pass down a new Func<Task<T>> which simply uses StartNew to run the user-provided Func<T>. The second constructor is a bit more fancy. Rather than taking a Func<T>, it takes a Func<Task<T>>. 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<Task<T>> 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 taskFactory delegate does very little work before returning the task instance. If, however, the taskFactory delegate does any non-negligable work, a call to Value would block until the call to taskFactory completes. To cover that case, the second approach is to run the taskFactory using Task.Factory.StartNew, i.e. to run the delegate itself asynchronously, just as with the first constructor, even though this delegate already returns a Task<T>.

    0 讨论(0)
提交回复
热议问题