【问题标题】:Confusing behaviour when invoking async methods inside ASP.NET在 ASP.NET 中调用异步方法时的行为令人困惑
【发布时间】:2012-09-23 23:46:31
【问题描述】:

我使用 Visual Studio 2012 创建了一个 ASP WebApplication。

如果我修改默认页面如下:

public partial class _Default : Page
{
    static async Task PerformSleepingTask()
    {
        Action action = () =>
        {
            Thread.Sleep(TimeSpan.FromSeconds(0.5));
            int dummy = 3; // Just a nice place to put a break point
        };
        await Task.Run(action);
    }


    protected void Page_Load(object sender, EventArgs e)
    {
        Task performSleepingTask = PerformSleepingTask();
        performSleepingTask.Wait();
    }
}

在调用 performSleepingTask.Wait() 时,它会无限期挂起。


有趣的是,如果我在 web.config 中设置:
<appSettings>

    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
</appSettings>

然后它确实有效。 Wait 函数等待睡眠在另一个线程上完成,然后继续。


谁能解释一下:

  • 为什么会挂起?
  • 为什么他们有一个叫做TaskFriendlySynchronizationContext的东西? (鉴于它会导致任务挂起,我不会称其为“友好”)

  • 是否有从页面处理程序方法调用async 方法的“最佳实践”?

这是我想出的一个可行的实现,但感觉就像是笨拙的代码:

    protected void Page_Load(object sender, EventArgs e)
    {
        ManualResetEvent mre = new ManualResetEvent(false);
        Action act = () =>
        {
            Task performSleepingTask = PerformSleepingTask();
            performSleepingTask.Wait();
            mre.Set();
        };
        act.BeginInvoke(null, null);
        mre.WaitOne(TimeSpan.FromSeconds(1.0));
    }

【问题讨论】:

  • 我想弄清楚为什么你想让一个线程在一个 aspx 页面上休眠
  • @Lawrence Johnson - 这是一个故意设计的示例,旨在说明行为。如果你愿意,你可以把它想象成“PerformExpensiveDatabaseTasks”。

标签: asp.net task-parallel-library async-await


【解决方案1】:

为什么会挂起?

代表PerformSleepingTaskTask 正在尝试在其await 之后恢复,以便PerformSleepingTask 可以返回。它正在尝试重新进入 ASP.NET 请求上下文,该上下文被对 Wait 的调用阻止。 This causes a deadlock, as I expound on my blog.

为避免死锁,请遵循以下最佳做法:

  1. 一直使用async。不要阻止 async 代码。
  2. 在您的“库”方法中使用ConfigureAwait(false)

为什么他们有一个叫TaskFriendlySynchronizationContext 的东西? (鉴于它会导致任务挂起,我不会称之为“友好”)

TaskFriendlySynchronizationContext 使用新的AspNetSynchronizationContext(.NET 4.0 TaskFriendlySynchronizationContext 已重命名为LegacyTaskFriendlySynchronizationContext),它还使用新的async 感知管道。

我不是 100% 确定这一点,但我怀疑Page_Load 适用于旧版 SyncCtx 的原因是旧管道尚未放置 SyncCtx。不过,我不确定它为什么会这样(除非 Page.Asyncfalse)。

是否有从页面处理程序方法调用异步方法的“最佳实践”?

Absolutely。您可以只创建事件处理程序async void,或使用RegisterAsyncTask(new PageAsyncTask(...));。第一种方法更简单,但第二种方法受到 ASP.NET 团队的青睐。

【讨论】:

  • 同样,如果您任何参与了您推荐的项目或您正在推送的链接(例如,您的博客),那么您必须 i> 提供披露。我看到183 posts 应该有这个披露,而这个肯定没有。不这样做可能会导致进一步的主持人行动。此外,您近 20% 的帖子都引用了您的博客。请注意常见问题解答中的部分内容,即“如果您的大量帖子用于宣传,这可能不适合您”。
  • 由于我每天只能进行 5 次编辑,这可能需要一段时间来清理。请耐心等待。
  • 当然可以,但是在构建答案时请记住这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-06
  • 2021-09-25
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多