不等待时配置await

本文关键字:await 配置 等待 | 更新日期: 2023-09-27 17:50:57

我有一个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上下文(?)
如果我误解了,有人能给我解释一下吗?

不等待时配置await

. NET同步上下文不允许在请求中启动即发即忘的工作项。运行时会主动监视这些东西,并尝试生成异常,因为这些代码模式会导致空引用、死锁、av和其他令人讨厌的东西。

如果你确实需要在ASP中启动即发即弃的工作。. NET,考虑使用WebBackgrounder。它与ASP集成。. NET可扩展点,这些可扩展点被设计为允许这样做。因此,它不会阻止活动请求,但请记住Stephen的警告:它根本不能保证执行。如果您需要保证执行,请考虑使用像Service Bus这样的可靠性机制。

如果您的场景是如何在加载过程中执行一些(相对)长时间运行的任务,那么ASP. jsNET通过Page允许这种场景。RegisterAsyncTask方法。Scott Hansleman在《在ASP中使用异步方法的魔力》中描述了如何使用它。. NET 4.5加上一个重要的gotcha

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

RegisterAsyncTask(new PageAsyncTask(MyAsyncMethod));

则呼叫Page。executerregisteredasynctasks开始执行所有注册的任务。

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

这在"在ASP中不要做什么"中也有描述。. NET, What to do instead"在"异步页面事件"部分

我不知道他为什么不把它贴出来,但是我的两个问题在Stephen Cleary的这篇博文中得到了回答:

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

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

  • 回答我的第一个要点,是的,在知道它们不会使用上下文的库方法中使用ConfigureAwait(false)是可以的(鼓励!),因为……
  • …为了回答我的第二个要点,即使库async方法抛弃了UI上下文,调用方法仍然有自己的副本。因此,调用方法可以await库方法并在UI上下文中恢复…但是当它发生时,它会死锁。