如何在后台继续执行任务、等待和异步
How to proceed with Task, await and async in background
我面临需要帮助以了解如何正确进行的情况。
我有一个行动是一种预算形式。用户填写完所有字段后,用户将保存预算并按下“发送”按钮。
发送按钮将保存数据,然后向所有公司发送电子邮件。当我说所有公司时,我指的是大约 1000 家公司。
但是,问题是当我按下发送按钮时,在为公司发送电子邮件时页面被锁定,在向所有公司发送电子邮件后,网站 return查看。
我想做的是,当用户按下“发送”按钮时,网站会节省预算,开始在后台发送电子邮件,并且几乎立即 return 向用户显示视图,但是, 与此同时,该网站正在后台向 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 的异步方法。
问题是,我如何简单地将发送电子邮件方法扔到后台和 return 视图,而不等待发送完成?
第二个问题是我正在使用一个框架来转换电子邮件中的视图,因此,为此我们需要控制器的上下文。
我应该使用 task.factory.startnew 吗?
我应该使用 Thread 吗?
考虑因素:
不幸的是,异步控制器操作在这种情况下没有帮助,因为它们在等待异步操作完成时不会对用户产生响应。他们只解决与线程池和应用程序容量相关的内部问题。
如果您需要 "Fire and Forget" 方法,请考虑使用 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)
}
});
有很多方法可以做到。例如,您可以简单地从上面的代码中删除 await 。或者您可以使用 LINQ 并让您的任务数组使用延续。
Task[] tasks = companies.Select(c => EmailFactory.SendBudget(proposta).SendAsync(c)).ToArray();
Task.Factory.ContinueWhenAll(tasks, t => { /*do something*/ });
(我假设您需要以某种方式将公司传递给每个 Send 方法调用)
或者如 Ahmed 所述,您可以启动一个线程并在其中发送电子邮件,以这种或其他方式。
由于您是通过 asp.net 托管的,正确的方法有点复杂。
通过线程排队工作不会通知 运行time 有工作要完成。这意味着当应用程序域被回收时(这是在没有您干预的情况下不时完成的),您排队的工作不能保证完成。如果这与您无关,请继续排队后台任务。
"proper" 方法是以某种方式坚持该工作(消息队列、sql 服务器等...)并让另一个主机 运行 它。
查看此 blog post 以获得更多 details/possible 解决方案。
我喜欢将需要发送的电子邮件排队,并让另一个服务实际执行发送的想法...
...特别是因为似乎每封电子邮件都在完成一些工作。
例如,您可以只在数据库中创建一个条目,其他服务从中工作,并将它们标记为已发送。如果您的电子邮件的内容实际上是通过调用视图生成的,那么单独的服务应该仍然能够通过 WebRequest 或类似的方式简单地调用相应的操作。
然后您还可以将工作量标记为已完成,并为该事实提供证据。
我面临需要帮助以了解如何正确进行的情况。
我有一个行动是一种预算形式。用户填写完所有字段后,用户将保存预算并按下“发送”按钮。
发送按钮将保存数据,然后向所有公司发送电子邮件。当我说所有公司时,我指的是大约 1000 家公司。
但是,问题是当我按下发送按钮时,在为公司发送电子邮件时页面被锁定,在向所有公司发送电子邮件后,网站 return查看。
我想做的是,当用户按下“发送”按钮时,网站会节省预算,开始在后台发送电子邮件,并且几乎立即 return 向用户显示视图,但是, 与此同时,该网站正在后台向 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 的异步方法。
问题是,我如何简单地将发送电子邮件方法扔到后台和 return 视图,而不等待发送完成?
第二个问题是我正在使用一个框架来转换电子邮件中的视图,因此,为此我们需要控制器的上下文。
我应该使用 task.factory.startnew 吗?
我应该使用 Thread 吗?
考虑因素: 不幸的是,异步控制器操作在这种情况下没有帮助,因为它们在等待异步操作完成时不会对用户产生响应。他们只解决与线程池和应用程序容量相关的内部问题。
如果您需要 "Fire and Forget" 方法,请考虑使用 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)
}
});
有很多方法可以做到。例如,您可以简单地从上面的代码中删除 await 。或者您可以使用 LINQ 并让您的任务数组使用延续。
Task[] tasks = companies.Select(c => EmailFactory.SendBudget(proposta).SendAsync(c)).ToArray();
Task.Factory.ContinueWhenAll(tasks, t => { /*do something*/ });
(我假设您需要以某种方式将公司传递给每个 Send 方法调用)
或者如 Ahmed 所述,您可以启动一个线程并在其中发送电子邮件,以这种或其他方式。
由于您是通过 asp.net 托管的,正确的方法有点复杂。
通过线程排队工作不会通知 运行time 有工作要完成。这意味着当应用程序域被回收时(这是在没有您干预的情况下不时完成的),您排队的工作不能保证完成。如果这与您无关,请继续排队后台任务。
"proper" 方法是以某种方式坚持该工作(消息队列、sql 服务器等...)并让另一个主机 运行 它。
查看此 blog post 以获得更多 details/possible 解决方案。
我喜欢将需要发送的电子邮件排队,并让另一个服务实际执行发送的想法...
...特别是因为似乎每封电子邮件都在完成一些工作。
例如,您可以只在数据库中创建一个条目,其他服务从中工作,并将它们标记为已发送。如果您的电子邮件的内容实际上是通过调用视图生成的,那么单独的服务应该仍然能够通过 WebRequest 或类似的方式简单地调用相应的操作。
然后您还可以将工作量标记为已完成,并为该事实提供证据。