延迟共享异步资源 - 澄清?

时间:2015-09-20 12:35:37

标签: c# async-await

我在斯蒂芬的书结尾处看到了这个例子。

此代码可以通过 more 访问,而不是一个线程。

static int _simpleValue;
static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(  
async () =>
{
   await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
   return _simpleValue++;
});

async Task GetSharedIntegerAsync()
{
    int sharedValue = await MySharedAsyncInteger.Value;
}

无论代码多少部分同时调用Value, Task<int>仅在创建一次并返回给所有来电者。

然后他说:

  

如果有不同的线程类型可以调用Value(例如,用户界面)   线程和线程池线程,或两个不同的ASP.NET请求   线程),那么总是执行异步可能会更好   在线程池线程上委托。

所以他建议使用以下代码使整个代码在线程池线程中运行:

static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(() => Task.Run(
async () =>
{
    await Task.Delay(TimeSpan.FromSeconds(2));
    return _simpleValue++;;
}));

问题:

我不明白第一个代码的问题是什么。延续将在线程池线程中执行(由于ConfigureAwait,我们不需要原始上下文)。

同样,任何线程的任何控制都会到达await,控件将返回给调用者。

我没有看到第二个代码试图解决的额外风险。

我的意思是 - 第一个代码中可能调用Value “的不同线程类型有什么问题?

1 个答案:

答案 0 :(得分:6)

  

&#34;可能称为Value&#34;的不同线程类型有什么问题?   在第一个代码?

该代码没有错误。但是,假设您有一些CPU绑定工作以及async初始化调用。想象一下这样的例子:

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;
});

现在,你没有守卫&#34;反对这种行动。我假设Stephan提到了UI线程,因为你不应该做任何超过50ms的操作。你永远不希望你的UI线程冻结。

当您使用Task.Run来调用代理人时,您可以从可能将长期委托传递给Lazy<T>的地方覆盖自己。

Stephan Toub在AsyncLazy中谈到这一点:

  

我们有一个新的AsyncLazy<T>来自Lazy<Task<T>>和。{   提供了两个构造函数。每个构造函数都有一个函数   来自调用者,就像Lazy<T>一样。第一个构造函数,在   事实上,采用与Lazy<T>相同的Func。而不是传递那个   Func<T>直接指向基础构造函数,但是,我们改为   传递一个新的Func<Task<T>>,它只使用StartNew来运行   用户提供的Func<T>。第二个构造函数更加花哨。   而不是Func<T>,它需要Func<Task<T>>。有了这个   功能,我们有两个很好的选择,如何处理它。首先   只是将函数直接传递给基础构造函数,   e.g:

public AsyncLazy(Func<Task<T>> taskFactory) : base(taskFactory) { }
  

该选项有效,但这意味着当用户访问Value时   此实例的属性将调用taskFactory委托   同步。如果taskFactory那可能是完全合理的   委托在返回任务实例之前做的工作很少。   但是,如果taskFactory代表做了任何不可忽略的工作,a   在对taskFactory的调用完成之前,对值的调用将会阻塞。至   涵盖这种情况,第二种方法是运行taskFactory使用   Task.Factory.StartNew,即以异步方式运行委托,   就像第一个构造函数一样,即使这个委托已经存在   返回Task<T>