首页 > 解决方案 > 是否使用带有异步任务和等待的 ConfigureAwait(false) 传播异常?

问题描述

我感觉我对异步等待编程有很好的把握,但是今天发生的事情让我感到困惑。我一直在寻找这个问题的答案一段时间,但无法在任何地方找到它。我已经阅读了很多关于异步等待编程的内容,但是我虚弱的头脑无法理解在这个特定场景中到底发生了什么。

我有这两种方法:

public async Task<IEnumerable<ServerStatus>> GetServersStatusAsync()
{
    var serverStatuses = new List<ServerStatus>();
    try
    {
        ServerStatusRequest request = new ServerStatusRequest();
        var serverStatusResponse = await GetAsync<ServerStatusResponse>(request).ConfigureAwait(false);
    }
    // I would expect the exception thrown from GetAsync to be caught here. But it doesn't always do that.
    catch (Exception ex)
    {
        _log.Error("Failed to retrieve server statuses.", ex);
    }
    return serverStatuses;
}

private async Task<T> GetAsync<T>(IRequest request)
{
    string respString = string.Empty;
    try
    {
        
        var requestUrl = request.GetQueryString(_apiToken);
        _log.Debug($"Sending a request to {requestUrl}");
        CancellationTokenSource tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(_timeoutInSec));

        //This call can throw an exception. It will always get caught inside this current try catch, 
        //but if after the await we are continuing on a different thread, 
        //the re-thrown/propagated exception is not caught by the calling method.
        var response = await _client.GetAsync(requestUrl, tokenSource.Token).ConfigureAwait(false);

        if (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.BadRequest)
        {
            respString = await response.Content.ReadAsStringAsync();
            var deserializedObject = JsonConvert.DeserializeObject<T>(respString);
            return deserializedObject;
        }
    }
    catch (Exception ex) when (ex is JsonReaderException || ex is JsonSerializationException)
    {
        throw new JsonSerializationException($"Failed to deserialize {respString}", ex) { Source = respString };
    }

    return default(T);
}

我在代码片段中添加了 2 条注释作为指针,但基本思想是:

在 GetServerStatusAsync 中,我用 try catch 包装了所有内容,因为我想在那里处理异常。我调用 GetAsync,它等待使用 ConfigureAwait(false) 调用 HttpClient.GetAsync。如果我们不再在初始线程/上下文中,来自 HttpClient.GetAsync 的调用返回异常时,它可以在我的 GetAsync 方法中被捕获,但不会传播到 GetServerStatusAsync。如果我删除 ConfigureAwait(false),它总是会像我预期的那样向下传播,但使用 ConfigureAwait(false),它更像是 50/50。

我的代码或我对异步等待的理解是否存在灾难性问题?

任何帮助深表感谢。

编辑:根据评论中的请求,我添加了一个简化版本的调用 GetServersStatusAsync 的方法以及我如何调用该方法(以一种火而忘记的方式,但 Login 是用 try catch 包装的,所以不应该是大事件)。

public async Task Login(Action<LoginResult> callback = null)
{

    LoginResult result = new LoginResult() { Success = false };
    try
    {
            var serverStatus = await GetServersStatusAsync();
            if (serverStatus.Any())
            {
                result.Success = true;
                callback?.Invoke(result);
                return;
            }
    }
    catch (Exception ex)
    {
        result.Message = Strings.UnknownException;
        _log.Error("Login failed due to unexpected exception", ex);
    }
    callback?.Invoke(result);
}
 _restClient.Login(OnLoginResponse);

标签: c#asynchronousasync-awaitconfigureawait

解决方案


推荐阅读