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?

推荐答案

首先,让我指出,在ASP.NET应用程序中,"即发即忘"几乎总是错误的.只有当你不关心DeleteFooAsync是否真正完成时,"发火并忘记"才是一种可以接受的方法.

如果你愿意接受这个限制,我有some code on my blog个任务将注册到ASP.NET运行时,它同时接受同步和异步工作.

您可以编写一次性包装器方法来记录异常,如下所示:

private async Task LogExceptionsAsync(Func<Task> code)
{
  try
  {
    await code();
  }
  catch(Exception exception)
  {
    m_log.Error("Call failed: " + exception.ToString());
  }
}

然后使用我博客中的BackgroundTaskManager:

BackgroundTaskManager.Run(() => LogExceptionsAsync(() => DeleteFooAsync()));

或者,你可以保留TaskScheduler.UnobservedTaskException,只需这样称呼它:

BackgroundTaskManager.Run(() => DeleteFooAsync());

Asp.net相关问答推荐

我可以强制刷新我的样式表文件吗?

我们可以在一个网页中使用多个表单吗?

逐步 ASP.NET 自动构建/部署

如何在asp.net中判断会话是否过期

如何使用三个按钮是否和取消显示确认alert ,如 MS Word 中所示

stream.CopyTo - 文件为空.网

页面在谷歌浏览器中加载两次

从后面的 asp.net 代码中读取表单身份验证 cookie

如何从 Asp.net Mvc-3 发送邮箱?

ASP.NET IIS Web.config [内部服务器错误]

不能在 DropDownList 中 Select 多个项目

如何在 ASP.NET 中获取原始请求正文?

如何在 ASP.Net Gridview 中添加确认删除选项?

Html.RenderAction 和 Html.Action 的区别

ContentResult 与字符串

ASP.NET:在 Response.Redirect(...) 之后代码会发生什么?

解耦 ASP.NET MVC 5 标识以允许实现分层应用程序

x-powered by是什么意思?

您可以从 web.config 文件中的其他位置提取 log4net AdoNetAppender 的 connectionString 吗?

使用 FileUpload Control 获取文件的完整路径