1

我有一种async方法可以用来卸载几秒钟的即发即弃的工作,以免减慢我的页面加载速度。这项工作需要一些常规设置和整理;我希望(快速)设置在抛出时同步抛出,但我不想强制整理在 ASP 上下文中运行,所以我ConfigureAwait正在等待的位上使用:

public Task FireAndForget()
{
    DoSetup();
    return FireAndForgetAfterSetup();
}

private async Task FireAndForgetAfterSetup()
{
    await AFewSecondsWorthOfWork().ConfigureAwait(false);
    DoTidyUp();
}

protected void btn_Click(object sender, EventArgs e)
{
    FireAndForget();
}

这看起来很奇怪,因为

  • FireAndForgetAfterSetup不应该真正关心它是否是从 ASP 上下文中调用的,那么为什么它必须是调用者ConfigureAwait呢?
  • 如果我改变主意并决定btn_Click应该等待FireAndForget完成,它是否已经丢弃了 ASP 上下文(?)

如果我有误解,有人可以向我解释吗?

4

3 回答 3

3

ASP.NET 同步上下文不允许从请求中启动即发即弃的工作项。运行时会主动监控这些事情,并会尝试生成异常,因为这些代码模式会导致空引用、死锁、AV 和其他讨厌的事情。

如果您绝对需要在 ASP.NET 中启动即发即弃的工作,请考虑使用WebBackgrounder。它与旨在实现这一点的 ASP.NET 扩展点集成。所以它不会阻止活动请求,但请记住斯蒂芬的警告:它根本不能保证被执行。如果您需要保证执行,请考虑像服务总线这样的可靠性机制。

于 2014-03-18T16:08:12.567 回答
1

如果您的场景是如何在加载期间执行一些(相对)长时间运行的任务,ASP.NET 通过Page.RegisterAsyncTask方法允许这种场景。Scott Hansleman 在 The Magic of using Asynchronous Methods in ASP.NET 4.5 中描述了如何使用它以及一个重要的陷阱

本质上,您创建了一个返回 Task 并调用的异步方法:

RegisterAsyncTask(new PageAsyncTask(MyAsyncMethod));

然后调用Page.ExecuteRegisteredAsyncTasks开始执行所有注册的任务。

Scott Hanselman(当然)很好地描述了为什么使用事件处理程序、任务或后台线程是一个坏主意。

这也在“异步页面事件”部分的“在 ASP.NET 中不应该做什么,应该做什么”中进行了描述

于 2014-03-18T16:53:03.343 回答
0

我不确定他为什么不发布它,但我的确切两个问题在Stephen Cleary 的这篇博文中得到了回答:

这个例子需要注意的重要一点是异步方法调用的每个“级别”都有自己的上下文。DownloadFileButton_Click 在 UI 上下文中启动,并调用 DownloadFileAsync。DownloadFileAsync 也在 UI 上下文中启动,但随后通过调用 ConfigureAwait(false) 退出其上下文。DownloadFileAsync 的其余部分在线程池上下文中运行。但是,当 DownloadFileAsync 完成并且 DownloadFileButton_Click 恢复时,它会在 UI 上下文中恢复。

一个好的经验法则是使用 ConfigureAwait(false),除非您知道您确实需要上下文。

  • 在回答我的第一个要点时,是的,可以(鼓励!)ConfigureAwait(false)在知道它们不会使用上下文的库方法中使用,因为......
  • ...在回答我的第二个要点时,即使库async方法丢弃了 UI 上下文,调用方法仍然有自己的副本。因此,调用方法可以await在 UI 上下文中调用库方法并恢复......但是当这种情况发生时它会死锁。
于 2014-03-18T17:23:29.870 回答