【问题标题】:C# Async ApiController route issue resolving Task.WhenAny with a Task.DelayC# Async ApiController 路由问题用 Task.Delay 解决 Task.WhenAny
【发布时间】:2019-02-20 23:41:29
【问题描述】:

在我的服务器上,我有一个为用户生成 pdf 的路由。当生成时间超过指定时间时,路由应该返回一个接受的状态代码,并在处理完成时向用户发送电子邮件。我遇到的问题是Task.Delay 没有得到尊重。无论我将PdfGenerationTimeLimitUntilEmail 设置多低,Wait.Any 仍然无法解决,直到createPdfTask 完成。有趣的是,当生成抛出异常或完成,并且时间已超过Task.Delay 时间时,执行继续返回Accepted Status。我怀疑罪魁祸首是死锁情况。

我已彻底阅读但无法应用于我的问题的相关问题: C#/.NET 4.5 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

我有什么明显的遗漏吗?或者我应该知道的关于线程上下文的一些事情?

 [HttpPost]
 [Route("foo")]
 public async Task<IHttpActionResult> Foo([FromBody] FooBody fooBody)
 {
        var routeUser = await ValidateUser();

        async Task<Stream> CeatePdfFile()
        {
            var pdf = await createPdfFromFooData(fooBody);

            return await pdfFileToStream(pdf);
        }

        var delay = Task.Delay(PdfGenerationTimeLimitUntilEmail);
        var createPdfTask = CeatePdfFile();
        var firstTaskResolved = await Task.WhenAny(createPdfTask , delay);

        if (firstTaskResolved == createPdfTask)
        {
            var pdfFileStream = await createPdfTask ;
            return new FileActionResult(pdfFileStream);
        }

        // Creating the PDF can take a long time, so just send an email when it's done
        async void SendEmail(CancellationToken token)
        {
            var pdfFileStreamToEmail = await createPdfTask ;
            _emailSender.SendDownloadEmail(routeUser.Email, pdfFileStreanToEmail);
        }

        HostingEnvironment.QueueBackgroundWorkItem(token => SendEmail(token));
        return StatusCode(HttpStatusCode.Accepted);
}

【问题讨论】:

  • 应该async void SendEmailasync Task SendEmail 吗?这样我就可以等待方法了。
  • 作为旁注(和意见问题),我更喜欢命名约定...Async,您可以将Async 后缀添加到任何返回Task(或Task&lt;T&gt;)的方法中。它有助于提高代码的可读性。示例:CreatePdfFileAsync() 可能还有 SendMailAsync
  • 我怀疑createPdfFromFooData 在让步之前正在执行长时间的阻塞操作。试试这个:var createPdfTask = Task.Run(() =&gt; CeatePdfFile());
  • @KevinGosse 有一个更简单、更可重用的解决方案 - 查看答案
  • 至于@Igor 的问题:这不是一个真正的问题——你绝对必须不要在许多情况下使用async void,包括MVC——MVC 的同步上下文主动阻止 async void - 如果你尝试,它会抛出异常

标签: c# .net async-await task


【解决方案1】:

如果 Kevin Gosse 是正确的,那么这里真正的问题是 createPdfFromFooDataCeatePdfFile 在第一个 await 之前做了很多工作:你可以添加一个 Task.Yield()人为地强制额外等待,本质上将其余工作推送到相关上下文的工作队列(或线程池,否则):

    async Task<Stream> CeatePdfFile()
    {
        await Task.Yield(); // force asynchronicity
        var pdf = await createPdfFromFooData(fooBody);

        return await pdfFileToStream(pdf);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多