如何使用异步和等待,以便它将创建一个不同的任务或每个请求

时间:2019-05-14 05:17:02

标签: c# asp.net-core async-await

我是asp.net核心和异步编程的新手。下面是我的代码

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    var work = await TaskQueue.DequeueAsync(stoppingToken);
    while (!stoppingToken.IsCancellationRequested)
    {
        redirect(work);
    }
}

public async Task redirect(Func<CancellationToken, Task> work)
{
    await work();
}

所以在这里,当我运行程序并对其进行调试时,光标首先进入ExecuteAsync方法,而从while循环进入重定向方法。 重定向方法仍然是异步的,它将等待工作方法。所以我认为它没有异步运行。

我希望当光标进入工作方法时,它将启动一个新任务(线程)。

2 个答案:

答案 0 :(得分:2)

  

当我运行程序并对其进行调试时

您正在观察的行为是Visual Studio 尝试,对您有所帮助。它具有处理异步方法的特殊代码,因此,当您说“进入下一行”时,它不会突然转移到某些完全不相关的方法。它将转到该方法的下一行

  

我希望当光标进入工作方法时,它将启动一个新任务(线程)。

任务不是线程。特别是,sns.distplot(meanopa, hist=True, kde=True, bins=20, color = 'darkblue', hist_kws={'edgecolor':'black'}, kde_kws={'linewidth': 4}) 方法创建的任务不会在线程上运行;它们只是状态对象,可以在async方法完成后通知其他代码。

我建议阅读我的async intro,并紧跟async best practices

答案 1 :(得分:1)

您尝试使用Async-Await的方式存在一些误解,网络上有足够的内容,以下是详细信息:

  

所以在这里,当我运行程序并对其进行调试时,光标首先进入ExecuteAsync方法,而从while循环进入重定向方法。重定向方法仍然是异步的,它将等待工作方法。所以我认为它不是异步运行的

await的作用是释放调用上下文,而处理在后台进行,这有助于系统保持响应速度,因此系统具有可伸缩性/线程池来满足进一步的请求,因此只有{{1} }不会阻塞调用线程/上下文,在调试器中(如您的代码所示)将等待完成。

  

现在await进行的处理是IO还是计算

如果它是work,那就是真正在服务器上使用异步处理的地方,因为线程池线程已释放,并且如果与IO一起使用,则不需要重新输入相同的上下文来处理响应,这是图书馆期望的。如果是计算的,那么它就可以像WPF这样的UI很好地工作,因为Ui线程保持响应能力,但是仍然可以从当前池中删除线程,并且可用性有限/稀缺。

  

关于您的代码

  • 在当前形式下,它是一个阻塞代码,因为在while循环中您正在调用ConfigureAwait(false),而redirect(work)又会调用await work,它会释放调用方,但要等到下一次Async操作完成之前,被执行
  • 理想的方法是将所有任务收集在List<Task>之类的集合中,当等待Task.WhenAll时,就是所有聚合的任务都在一起,而您异步地等待单个代表Task对于所有这些对象而言,仍然不能像当前代码中那样盲目地执行/调用,而是阻止完整的聚合列表,该列表由@TheGeneral在注释中指定,从而有助于更好地处理所有任务,因为所有任务在一起>
  • await work()也不正确,编译错误,这需要一个取消令牌作为I / p参数
  • 在当前代码中,理想情况下还应等待redirect(work),整个调用链中都应异步等待
  

代码的修改版本

假设您需要Task.WhenAll并且您正在执行基于IO的异步操作,则以下应为您的代码:

protected override async Task ExecuteAsync(CancellationToken stoppingQueueToken,CancellationToken stopWorkToken)
{
      var work = await TaskQueue.DequeueAsync(stoppingQueueToken);
      List<Task> taskList = new List<Task>();
      while (!stoppingQueueToken.IsCancellationRequested)
      {  
         taskList.Add(redirect(work, stopWorkToken));
      }

      await Task.WhenAll(taskList);
}

public async Task redirect(Func<CancellationToken, Task> work, CancellationToken stopWorkToken)
{
    await work(stopWorkToken);
}

我已经为引入工作取消而引入了额外的取消令牌,因为工作处理仅在退出循环时才开始一次,在此之前它仅聚合任务,然后将它们一起在后台处理,并假定IO为最大效益。这里调试器也会阻塞,但是对于所有任务,只需一次调用

  

如果坚持使用当前代码,则只需进行以下更改:

protected override async Task ExecuteAsync(CancellationToken stoppingQueueToken)
{
    var work = await TaskQueue.DequeueAsync(stoppingQueueToken);
    List<Task> taskList = new List<Task>();
    while (!stoppingQueueToken.IsCancellationRequested)
    {
        await redirect(work, stoppingQueueToken);
    }
}

public async Task redirect(Func<CancellationToken, Task> work, CancellationToken stoppingQueueToken)
{
    await work(stoppingQueueToken);
}
  • 这段代码接连进行,仍然异步并释放调用上下文,但不是全部。如您所见,调试器将阻止。
  

更多细节

  • TaskThread不同并且可以互换,多个任务可以在同一线程上执行
  • 取消令牌是协作的,它不会取消已经启动的任务,应该在任务开始之前取消它,或者需要显式检查并抛出以停止任务执行,ThrowIfCancellationRequested