我有一个Web API的操作,我需要运行一些任务而忘记这个任务。 这就是我的方法现在的组织方式:
public async Task<SomeType> DoSth()
{
await Task.Run(...);
.....
//Do some other work
}
事情是,它显然会在等待线停止等待它完成后才会继续工作。 我需要“开除并忘记” 我应该在没有任何async-await的情况下调用Task.Run()吗?
答案 0 :(得分:22)
我需要“开除并忘记”
我有一篇博文,详细介绍了fire-and-forget on ASP.NET的几种不同方法。
总结:首先,尽量不要忘掉。这几乎总是一个坏主意。你真的想要“忘记”吗?如在,不关心它是否成功完成?忽略任何错误?在没有任何日志通知的情况下接受偶尔“丢失工作”?几乎总是,答案是否定的,“即发即忘”不是合适的方法。
可靠的解决方案是构建适当的分布式架构。也就是说,构造表示要完成的工作的消息,并将该消息排队到可靠的队列(例如,Azure队列,MSMQ等)。然后有一个独立的后端来处理该队列(例如,Azure WebJob,Win32服务等)。
我应该在没有任何async-await的情况下调用Task.Run()吗?
没有。这是最糟糕的解决方案。如果您必须做“一劳永逸”,并且您不愿意构建分布式体系结构,那么请考虑使用Hangfire。如果这对您不起作用,那么在非常,您应该通过HostingEnvironment.QueueBackgroundWorkItem
或我的ASP.NET Background Tasks library向ASP.NET运行时注册您的牛仔背景工作。请注意,QBWI和AspNetBackgroundTasks都是不可靠的解决方案;他们只是最大限度地减少你失去工作的机会,而不是阻止。
答案 1 :(得分:6)
在asp.net中真正的火灾和忘记任务可能很困难,因为它们经常会因为它们是作为其一部分创建的请求而死亡。
如果您使用的是4.5.2+,则可以使用QueueBackgroundWorkItem来运行任务。通过这种方法注册任务,AppDomain将尝试延迟关闭,直到它们全部完成为止,但仍有可能在它们完成之前被杀死的实例。这可能是最简单的事情,但值得一读,以确切了解哪些实例可能导致作业被取消。
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
await Task.Run(...);
});
有一个名为hangfire的工具,它使用持久存储来确保任务已完成,并具有内置的重试和错误记录功能。这更适用于“后台任务”,但确实适合火灾和遗忘。这相对容易设置并提供各种后备存储,我不记得确切的细节,但有些需要许可证,有些则不需要(如MSSQL)。
答案 2 :(得分:5)
对于火灾和遗忘,请使用此
Task.Factory.StartNew(async () =>
{
using (HttpClient client = new HttpClient())
{
await client.PostAsync("http://localhost/api/action", new StringContent(""));
}
});
答案 3 :(得分:3)
答案 4 :(得分:2)
永远不要忘记,因为那样你就不会看到任何错误,这会导致一些非常尴尬的故障排除,如果出现问题(让任务方法做自己的异常处理并不能保证工作,因为任务可能无法在第一时间成功启动)。除非你真的不介意任务是否做任何事情,但这很不寻常(因为,如果你真的不关心,为什么要执行任务)第一名)?至少,使用continuation创建您的任务:
Task.Run(...)
.ContinueWith(t =>
logException(t.Exception.GetBaseException()),
TaskContinuationOptions.OnlyOnFaulted
)
;
根据您的需要,您可以使其更加复杂。
在Web API的特定情况下,您可能实际上希望在完成请求之前等待后台任务完成。如果你不这样做,你就会留下在后台运行的东西,这些东西可能会歪曲你的服务真正承担的负担,甚至如果客户发出太多请求而你也不会完全停止工作什么东西来扼杀他们您可以收集任务并在最后发出await Task.WhenAll(...)
来实现这一目标;这样,你可以继续做有用的工作,同时你的后台任务就会消失,但是在你完成所有事情之前你都不会回来。
答案 5 :(得分:1)
对于调用即忘WebApi方法,我使用了以下代码以确保其返回OK响应。就我而言,登录时创建的承载授权令牌存储在cookie中:
...
FireAndForget().Wait();
...
private async Task FireAndForget()
{
using (var httpClient = new HttpClient())
{
HttpCookie cookie = this.Request.Cookies["AuthCookieName"];
var authToken = cookie["AuthTokenPropertyName"] as string;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
using (var response = await httpClient.GetAsync("http://localhost/api/FireAndForgetApiMethodName"))
{
//will throw an exception if not successful
response.EnsureSuccessStatusCode();
}
}
}
答案 6 :(得分:0)
我同意其他人的意见,你不应该忘记你的电话。但是,要回答您的问题,如果您从Task.Run()行中删除等待,则调用将不会被阻止,如图所示here
public async Task<SomeType> DoSth()
{
Task.Run(...);
.....
//Do some other work while Task.Run() continues in parallel.
}
答案 7 :(得分:0)
这里有一些帖子会推销“永不放火,勿忘”,错误等等。
事实是,从某些角度来看这没错。宁愿以某种方式被误解。
这应该真正说明的是“用户应该能够开除并忘记,但仍应告知开发者/所有者。”
一个很好的例子是网站上的与我们联系表格。客户填写表格,并在后台向网站所有者发送电子邮件。一些电子邮件服务器需要很长时间才能处理发送,因此像Hangfire这样的程序包(我将使用该程序包)将其发送到Web服务器“线程”之外的另一个进程。
是的,如果出现错误,这应该以某种方式建议开发人员/所有者(并保留联系方式)。但这绝不应告知潜在的联系人有关此问题的信息。 (除非您想失去潜在客户)