异步等待处理程序死锁

7
我被卡在了异步死锁中,无法找到正确的语法来解决它。我已经查看了几种不同的解决方案,但似乎无法完全弄清楚问题的原因。
我正在使用Parse作为后端,并尝试使用处理程序写入表格。我的处理程序大致如下:
public class VisitorSignupHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        //Get the user's name and email address
        var UserFullName = context.Request.QueryString["name"].UrlDecode();
        var UserEmailAddress = context.Request.QueryString["email"].UrlDecode();

        //Save the user's information
        var TaskToken = UserSignup.SaveUserSignup(UserFullName, UserEmailAddress);
        TaskToken.Wait();
        ....

    }

    public bool IsReusable { get { return false; } }
}

然后它正在调用我的中间层:
public static class UserSignup
{
    public static async Task SaveUserSignup(string fullName, string emailAddress)
    {
        //Initialize the Parse client with the Application ID and the Windows key
        ParseClient.Initialize(AppID, Key);

        //Create the object
        var UserObject = new ParseObject("UserSignup")
                            {
                                {"UserFullName", fullName},
                                {"UserEmailAddress", emailAddress}
                            };

        //Commit the object
        await UserObject.SaveAsync();
    }
}

尽管似乎会在Wait()处卡住,但我原以为Wait()只会等待任务完成,然后返回正常操作。这不正确吗?
2个回答

14
你遇到了一个常见的死锁问题,我在我的博客最近的MSDN文章中有所描述。
简而言之,默认情况下await会在一个已捕获的“上下文”中恢复其异步方法,在ASP.NET上,这个“上下文”一次只允许一个线程进入。因此,当你调用Wait时,你正在阻塞该上下文中的一个线程,而当await准备好恢复异步方法时,它无法进入该上下文。因此,上下文中的线程被阻塞在Wait(等待异步方法完成),而异步方法被阻塞在等待上下文空闲...死锁。
为了解决这个问题,你应该“始终异步”。在这种情况下,使用HttpTaskAsyncHandler代替IHttpHandler
public class VisitorSignupHandler : HttpTaskAsyncHandler
{
  public override async Task ProcessRequestAsync(HttpContext context)
  {
    //Get the user's name and email address
    var UserFullName = context.Request.QueryString["name"].UrlDecode();
    var UserEmailAddress = context.Request.QueryString["email"].UrlDecode();

    //Save the user's information
    var TaskToken = UserSignup.SaveUserSignup(UserFullName, UserEmailAddress);
    await TaskToken;
    ....

  }
}

谢谢!在发布这篇文章之前,我阅读了你的博客,但是就是无法弄清楚语法。这个很好用。感激不尽。 - Barry Tormey
1
不客气!顺便提一下,请好好阅读TAP文档,这里有一些适用于此处的async约定,特别是将SaveUserSignup重命名为SaveUserSignupAsync - Stephen Cleary
你能帮我看一下这个问题吗?https://dev59.com/7LDma4cB1Zd3GeqPCNSO - Pரதீப்

1

你的问题在于混合使用同步和异步代码。虽然这是可行的,但比较棘手。最好的方法是将你的http处理程序也变为异步:

public class VisitorSignupHandler : HttpTaskAsyncHandler
    {
        public override async Task ProcessRequestAsync(HttpContext context)
        {
           //Get the user's name and email address
           var UserFullName = context.Request.QueryString["name"].UrlDecode();
           var UserEmailAddress = context.Request.QueryString["email"].UrlDecode();

           //Save the user's information
           await UserSignup.SaveUserSignup(UserFullName, UserEmailAddress);
..

        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接