为什么 HttpClient 似乎在没有 .Result 的情况下挂起?

Why does HttpClient appear to hang without .Result?

我有这段代码可以调用 API,其中 return 是一个令牌。但是,如果我替换此行,它只会 return:

var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);

这一行:

var response = TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent).Result;

为什么?

public static async Task<Token> GetAPIToken()
{
    if (DateTime.Now < Token.Expiry)
        return Token;

    TokenClient.DefaultRequestHeaders.Clear();
    TokenClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    TokenClient.BaseAddress = new Uri(EnvironmentHelper.BaseUrl);
    TokenClient.Timeout = TimeSpan.FromSeconds(3);

    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("grant_type", "client_credentials"),
        new KeyValuePair<string, string>("client_id", EnvironmentHelper.APITokenClientId),
        new KeyValuePair<string, string>("client_secret", EnvironmentHelper.APITokenClientSecret)
    });

    try
    {
        // HANGS - this next line will only ever return if suffixed with '.Result'
        var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);

        var content = await response.Content.ReadAsStringAsync();
        dynamic jsonContent = JsonConvert.DeserializeObject(content);

        Token token = new Token();
        token.AccessToken = jsonContent.access_token;
        token.Type = jsonContent.token_type;
        token.Expiry = DateTimeOffset.Now.AddSeconds(Convert.ToDouble(jsonContent.expires_in.ToString()) - 30);
        token.Scope = Convert.ToString(jsonContent.scope).Split(' ');

        Token = token;
    }
    catch (Exception ex)
    {
        var m = ex.Message;
    }

    return Token;
}

The top-most code where awaiting is not performed has this: var wm = new WebRepo().GetWeb(Id).Result

您正在 运行进入 deadlock since the synchronous code is blocking on async code. The best solution is to remove the blocking and go async all the way

The calling code is a public static property with a getter, referenced in over 100 places. I'm not sure how best to convert the property to async friendly

a few approaches to async properties,这取决于 属性 的语义。如果每次调用都是re-evaluates,那么它就是变相的方法,应该变成async Task<T>方法。如果设置一次,那么AsyncLazy<T>可能是更好的选择。

Is there a way to wrap one of the calls? It is a mammoth task to go and refactor that much code.

没有适用于所有情况的包装。但是,有一些 techniques for mixing sync and async code 可以适用于特定场景。

以下是我会考虑的优先顺序:

  1. 让它一直异步。重构代码,让它变得更好。 (需要大量修改代码,目前可能不可行)。
  2. 让它一直同步。将 GetAPIToken(及其调用者)更改为同步而不是异步。 (要求所有调用代码同步,这可能是不可能的)。
  3. 使用 boolean argument hack 使 GetAPIToken 可同步和异步调用。 (需要改变的能力GetAPIToken)。
  4. 在线程池线程上异步使用 thread pool hack 到 运行 GetAPIToken 并同步阻塞另一个线程。