使用带有异步任务和等待的ConfigureAwait(false) 是否会传播异常?

2024-02-28

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

我有这两种方法:

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;
}

And

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 的方法的简化版本以及如何调用该方法(以一劳永逸的方式,但登录是用 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);

但使用ConfigureAwait(false) 则更像是50/50。

我相信默认等待者和基于ConfigureAwait(false)的异常处理的内部机制以相同的方式工作。 因此,如果您提供问题的最小可重现示例,那就太好了。 我试图遵循你的模式:

  1. 将几个异常转换为另一个异常。

  2. 按原样隐式抛出第三个。

  3. 将所有这些都放在堆栈的较高位置。

    foreach(int v in Enumerable.Range(1,10))
    try
    {
        await ExceptionDemo(v).ConfigureAwait(false);
    }
    catch (Exception e)
    {
        Console.WriteLine($"{e.GetType().Name}: {e.Message}");
    }
    
    
    
    static async Task ExceptionDemo(int value)
    {
    await Task.Delay(1000);
    try
    {
        throw Random.Shared.Next(0, 3) switch
        {
            0 => new ArgumentNullException($"{nameof(ArgumentNullException)}-{value}", (Exception?)null),
            1 => new InvalidOperationException($"{nameof(InvalidOperationException)}-{value}"),
            2 => new StackOverflowException($"{nameof(StackOverflowException)}-{value}")
        };
    }
    catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException)
    {
        throw new ApplicationException(ex.Message);
    }
    }
    

并捕获了所有异常。 Async/await 不关心抛出异常的方式,也不关心当前线程中的异常,也不关心异步执行时的连续线程中的异常。

UPDATE

如果添加一个空的 catch 块,它会吞掉一些异常,因此,在我的例子中,有一些遗漏的异常:

ApplicationException: InvalidOperationException-1
ApplicationException: InvalidOperationException-2
ApplicationException: InvalidOperationException-3
ApplicationException: InvalidOperationException-4
ApplicationException: InvalidOperationException-6
ApplicationException: InvalidOperationException-9
ApplicationException: ArgumentNullException-10

但在你的情况下,它们都应该被隐式抛出,并且全部被捕获。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用带有异步任务和等待的ConfigureAwait(false) 是否会传播异常? 的相关文章

  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 如何将图像和 POST 数据上传到 Azure 移动服务 ApiController 终结点?

    我正在尝试上传图片and POST表单数据 尽管理想情况下我希望它是json 到我的端点Azure 移动服务应用 我有ApiController method HttpPost Route api upload databaseId sea
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif
  • 使用 libcurl 检查 SFTP 站点上是否存在文件

    我使用 C 和 libcurl 进行 SFTP FTPS 传输 在上传文件之前 我需要检查文件是否存在而不实际下载它 如果该文件不存在 我会遇到以下问题 set up curlhandle for the public private ke
  • 恢复上传文件控制

    我确实阅读了以下帖子 C 暂停 恢复上传 https stackoverflow com questions 1048330 pause resume upload in c 使用 HTTP 恢复上传 https stackoverflow

随机推荐