异步函数在WebMethod中死锁,但如果在Page_Load中调用,则可以正常工作
本文关键字:常工作 工作 死锁 WebMethod 函数 如果 Load 异步 Page 调用 | 更新日期: 2023-09-27 18:25:59
我有一个WebForms项目,它正在尝试使用一些async
代码。在早期开发过程中,我只是在Page_Load
(也是async
)中调用我的async
代码,并将结果写入Response
。很好,易于测试。一切如愿以偿。
代码可以总结为:
- 获取Azure授权令牌
- 向Azure的REST API发出HTTP请求
- 将结果返回给客户端
因此,当我试图在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
中不调用此代码时会出现此问题?我能做些什么来解决这个问题?
更改
IRestResponse<JObject> response = await client.Execute<JObject>(request);
至
IRestResponse<JObject> response = await client.Execute<JObject>(request).ConfigureAwait(false);
这对你有帮助吗?问题是两个"等待"都在等待同时捕获相同的上下文,ConfigureWait阻止了这个(读取其文档)