C#异步ApiController路由问题解决了Task.WhenAny和Task.Delay

时间:2019-02-20 15:15:26

标签: c# .net async-await task

在我的服务器上,我有一条为用户生成pdf的路由。当生成花费的时间超过指定的时间量时,该路由应返回接受的状态代码,并在处理完成后向用户发送电子邮件。我遇到的问题是Task.Delay没有得到遵守。无论我将PdfGenerationTimeLimitUntilEmail设置得多么低,Wait.Any仍然无法解析,直到createPdfTask完成。有趣的是,当世代引发异常或完成,并且时间已超过Task.Delay时间时,执行将继续以返回“接受状态”。我怀疑罪魁祸首是僵局。

一个相关的问题,我已经仔细阅读,但无法解决我的问题: 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);
}

1 个答案:

答案 0 :(得分:2)

如果凯文·高斯(Kevin Gosse)是正确的,那么这里的真正问题是createPdfFromFooDataCeatePdfFile在第一个await之前做了大量工作:可以添加Task.Yield()来人为地强制进行额外的等待,实际上将其余工作推到相关上下文(或线程池,否则)的工作队列中:

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

        return await pdfFileToStream(pdf);
    }