异步函数在WebMethod中死锁,但如果在Page_Load中调用,则可以正常工作

本文关键字:常工作 工作 死锁 WebMethod 函数 如果 Load 异步 Page 调用 | 更新日期: 2023-09-27 18:25:59

我有一个WebForms项目,它正在尝试使用一些async代码。在早期开发过程中,我只是在Page_Load(也是async)中调用我的async代码,并将结果写入Response。很好,易于测试。一切如愿以偿。

代码可以总结为:

  1. 获取Azure授权令牌
  2. 向Azure的REST API发出HTTP请求
  3. 将结果返回给客户端

因此,当我试图在WebMethod中调用这个async方法时,我感到非常惊讶,但它失败了。它似乎在其他async代码(特别是Azure AD AuthenticationContext.AcquireTokenAsync方法)上陷入僵局。

我的第一个想法是一些上下文魔法,但ConfigureAwait(false)没有任何效果。所以我不知道有什么不同。我试过WebMethod是否为async,结果没有差异。快速测试表明,这个WebMethod的配置方式没有什么问题——让我的代码提前返回(在调用AD代码之前)可以使它按预期工作。尽管那时没有发生任何异步事件。

我还尝试使用非异步方法来获取Azure AD令牌。这很好,但当我的应用程序尝试发出HTTP请求时(使用RestSharp.Portable库,该库只提供async方法),它会被卡住。我的代码看起来像:

protected async Task<JObject> PerformRequest(string path, Method method, string apiVersion, string requestBody)
{
    RestClient client = new RestClient("https://management.azure.com");
    RestRequest request = new RestRequest(path, method);
    request.AddParameter("api-version", apiVersion);
    request.AddHeader("Authorization", await this.GetAccessTokenAsync());
    IRestResponse<JObject> response = await client.Execute<JObject>(request);
    return response.Data;
}

我的工作Page_Load和非工作WebMethod使用的代码几乎相同,只是WebMethod返回结果,而Page_Load将结果写入Response

GetAccessToken看起来像:

public async Task<string> GetAccessTokenAsync()
{
    string tenantId = "xxx";
    AuthenticationContext authenticationContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
    ClientCredential credential = new ClientCredential(clientId: "xxx", clientSecret: "xxx=");
    AuthenticationResult result = await authenticationContext.AcquireTokenAsync(resource: "https://management.core.windows.net/", clientCredential: credential);
    if (result == null) throw new InvalidOperationException("Failed to obtain the JWT token");
    return this.accessToken = result.AccessTokenType + " " + result.AccessToken;
}

对所有这些的调用都是直接在WebMethod中进行的(这是WebMethod当前唯一做的事情):

return await this.PerformRequest("xxx", Method.GET, "xxx", null).ContinueWith(result => result.ToString());

为什么我的应用程序在WebMethod中调用此代码而在Page_Load中不调用此代码时会出现此问题?我能做些什么来解决这个问题?

异步函数在WebMethod中死锁,但如果在Page_Load中调用,则可以正常工作

更改

IRestResponse<JObject> response = await client.Execute<JObject>(request);

IRestResponse<JObject> response = await client.Execute<JObject>(request).ConfigureAwait(false);

这对你有帮助吗?问题是两个"等待"都在等待同时捕获相同的上下文,ConfigureWait阻止了这个(读取其文档)