The general answers such as here and here to fire-and-forget questions is not to use async/await, but to use Task.Run
or TaskFactory.StartNew
passing in the synchronous method instead.
However, sometimes the method that I want to fire-and-forget is async and there is no equivalent sync method.
Update Note/Warning:正如斯蒂芬·克利里(Stephen Cleary)在下面指出的,在你发送了回复之后继续处理请求是危险的.原因是AppDomain可能会在工作仍在进行时关闭.更多信息请参见他回复中的链接.总之,我只是想提前指出这一点,这样我就不会让任何人走上错误的道路.
我认为我的情况是合理的,因为实际工作是由不同的系统(不同服务器上的不同计算机)完成的,所以我只需要知道消息是否已留给该系统.如果出现异常,服务器或用户对此无能为力,并且不会影响用户,我所需要做的就是查阅异常日志(log)并手动清理(或实现某种自动机制).如果AppDomain关闭,我将在远程系统中保留一个剩余文件,但我会在通常的维护周期中拾取该文件,并且由于我的Web服务器(数据库)不再知道它的存在,并且其名称带有唯一的时间戳,因此在它仍然存在时不会引起任何问题.
如果我能像Stephen Cleary指出的那样访问持久性机制,那将是理想的,但不幸的是,我现在没有.
我考虑过假装DeleteFoo请求在客户端(javascript)上完成得很好,同时保持请求的打开状态,但我需要响应中的信息才能继续,这样会耽误时间.
所以,最初的问题...
例如:
//External library
public async Task DeleteFooAsync();
在我的ASP.NETMVC代码中,我想以"一发即忘"的方式调用DeleteFooAsync-我不想拖延响应,等待DeleteFooAsync完成.如果DeleteFooAsync由于某种原因失败(或抛出异常),用户或程序对此无能为力,所以我只想记录一个错误.
现在,我知道任何异常都会导致未观察到的异常,所以我能想到的最简单的情况是:
//In my code
Task deleteTask = DeleteFooAsync()
//In my App_Start
TaskScheduler.UnobservedTaskException += ( sender, e ) =>
{
m_log.Debug( "Unobserved exception! This exception would have been unobserved: {0}", e.Exception );
e.SetObserved();
};
这样做有风险吗?
我能想到的另一个 Select 是制作我自己的包装,比如:
private void async DeleteFooWrapperAsync()
{
try
{
await DeleteFooAsync();
}
catch(Exception exception )
{
m_log.Error("DeleteFooAsync failed: " + exception.ToString());
}
}
然后把它称为TaskFactory.StartNew(可能是在异步操作中包装).然而,每次我想以一种"火而忘"的方式调用一个异步方法时,这似乎都需要很多包装器代码.
我的问题是,以"即火即忘"的方式称异步方法为正确的方式是什么?
UPDATE:
嗯,我在我的控制器中发现了以下内容(并不是控制器操作需要是异步的,因为还有其他异步调用在等待):
[AcceptVerbs( HttpVerbs.Post )]
public async Task<JsonResult> DeleteItemAsync()
{
Task deleteTask = DeleteFooAsync();
...
}
导致表单出现异常:
未处理的异常:系统.NullReferenceException:对象引用
这在第here章中讨论过,似乎与SynchronizationContext和"在所有异步工作完成之前,返回的任务已转换为终端状态"有关.
因此,唯一有效的方法是:
Task foo = Task.Run( () => DeleteFooAsync() );
我对此的理解是,StartNew为DeleteFooAsync获得了一个新线程来处理.
遗憾的是,在这种情况下,Scott下面的建议不适用于处理异常,因为foo不再是DeleteFooAsync任务,而是来自Task.Run的任务,因此不处理来自DeleteFooAsync的异常.我的UnobservedTaskException最终确实会被调用,所以至少它仍然有效.
所以,我想问题仍然存在,如何在asp中启动并忘记异步方法.网络mvc?