如何在后台进行Task, await和async

本文关键字:await async Task 后台 | 更新日期: 2023-09-27 18:15:17

我正面临一种需要帮助来理解如何正确进行的情况。

我有一个Action,它是一种预算形式。用户填写完所有字段后,用户将按发送按钮保存预算。

发送按钮将保存数据,然后,将发送电子邮件给所有公司。我说的所有公司是指大约1000家公司。

但是,问题是当我按下Send按钮时,在为公司发送电子邮件时页面被锁定,在向所有公司发送电子邮件后,网站返回视图。

我想做的是,当用户按下发送按钮时,网站保存预算,开始在后台发送电子邮件,并几乎立即返回视图给用户,然而,与此同时,网站正在后台发送电子邮件给1000家公司。

我是怎么做到的?这是我的方法的签名:

public async Task<PartialViewResult> EnviarProposta(PostOrcamentoServicoProposta proposta)
{
    // persist the budget
    SaveData(proposta);
    // get all companies...
    var companies = getCompanies(proposta);
    foreach (var company in companies)
        await EmailFactory.SendBudget(proposta, company).SendAsync();
    return PartialView(proposta);
}

SendAsync是SMTP的异步方式。

问题是,如何简单地将send e-mail方法扔到后台并返回视图,而不等待发送完成?

第二个问题是,我正在使用一个框架来转换电子邮件中的视图,因此,我们需要控制器的上下文。

我应该使用task.factory.startnew吗?

我应该使用线程吗?

注意事项:不幸的是,异步控制器动作在这种情况下没有帮助,因为它们在等待异步操作完成时不会向用户提供响应。它们只解决与线程池和应用程序容量相关的内部问题。

如何在后台进行Task, await和async

由于您是通过asp.net托管的,正确的方法有点复杂。

通过线程排队工作不会通知运行时有工作要做。这意味着当应用程序域被回收时(在没有您干预的情况下不时进行),您排队的工作不能保证完成。如果你不关心这个问题,可以继续排队执行一个后台任务。

"正确"的方法是以某种方式(消息队列,sql server等)持久化工作,并让另一个主机运行它。

我喜欢将需要发送的电子邮件排队,并让另一个服务实际执行发送的想法…

…特别是因为看起来每封邮件都在做一些工作。

例如,您可以在数据库中创建一个条目,其他服务从该条目开始工作,并将其标记为已发送。如果电子邮件的内容实际上是由调用视图生成的,那么单独的服务应该仍然能够通过WebRequest或类似的方法简单地调用相应的Action。

然后您还可以将工作负载标记为已完成,并有证据证明这一事实。

有很多方法可以做到。例如,您可以简单地从上面的代码中删除await。或者你可以使用LINQ,让你的任务数组使用continuation。

Task[] tasks = companies.Select(c => EmailFactory.SendBudget(proposta).SendAsync(c)).ToArray();
Task.Factory.ContinueWhenAll(tasks, t => { /*do something*/ });

(我假设您需要以某种方式将company传递给每个Send方法调用)

或者正如Ahmed提到的,你可以创建一个线程,用这种或那种方式发送电子邮件。

如果你需要一个"即发即弃"的方法,可以考虑使用Task.Factory.StartNew()。

Task.Factory.StartNew( () => 
{
    foreach(Company c in companies)
    {
        // send your email here, make sure you have good error handling!
        EmailFactory.SendBudget(proposta)
    }
});