并发编程 --- 异步方法的异常处理

这篇具有很好参考价值的文章主要介绍了并发编程 --- 异步方法的异常处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

现在模拟一个异步方法抛出了异常:

public static async Task ThrowAfter(int ms, string message)
{
    await Task.Delay(ms);
    throw new Exception(message);
}

思考一下, DontHandle() 方法是否能够捕获到异常?

public static void DontHandle()
{
    try
    {
        ThrowAfter(1000, "first");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

}

答案是:不会捕获到异常!

因为 DontHandle() 方法在 ThrowAfter() 方法抛出异常之前,就已经执行完毕。

异步方法的异常处理

那么上述代码怎么才能捕获到异常呢?

若想要捕获异常则必须通过 await 关键字等待 ThrowAfter() 方法执行完成。

将上文中的代码段进行修改:

public static async void HandleoOnError()
{
    try
    {
        await ThrowAfter(1000, "first");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

结果就会输出:

first

多个异步方法的异常处理

如果调用两个异步方法,每个都会抛出异常,该如何处理呢?

我们可以这样写:

public static async void StartTwoTasks()
{
    try
    {
        await ThrowAfter(1000, "first");
        await ThrowAfter(1000, "second");
        Console.WriteLine("StartTwoTasks is Complate");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

思考一下输出是什么?

答案是:

first

并没有预想中的两个异常都捕获打印出来,也没有看到“StartTwoTasks is Complate”这句话打印出来。因为使用 await 关键字之后,两次调用 ThrowAfter() 方法就变成了同步执行,捕获到第一次的异常之后直接进入到 catch 代码段,不再执行后续代码。

可以尝试解决这个问题,使用 Task.WhenAll() 方法,该方法不管任务是否抛出异常,都会等到两个任务完成。如下代码:

public static async void StartTwoTasksParallel()
{
    try
    {
        Task t1 = ThrowAfter(1000, "first");
        Console.WriteLine("t1 is Complate");
        Task t2 = ThrowAfter(1000, "second");
        Console.WriteLine("t2 is Complate");
        await Task.WhenAll(t2, t1);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

输出:

t1 is Complate
t2 is Complate
second

从输出可以看出来,使用 WhenAll() 方法,两个任务都是执行完成的,但是,捕获异常只能捕获 WhenAll()方法参数中,排在最前面的,且第一个抛出异常的任务的消息,

上述方式有缺陷,只能抛出一个异常的任务的消息,可以将上面的方式再进化一下,如下代码:

public static async void StartTwoTasksParallelEx()
{
    Task t1 = null;
    Task t2 = null;
    try
    {
        t1 = ThrowAfter(1000, "first");
        t2 = ThrowAfter(1000, "second");
        await Task.WhenAll(t2, t1);

    }
    catch (Exception ex)
    {
        if (t1.IsFaulted)
        {
            Console.WriteLine(t1.Exception.InnerException.Message);
        }

        if (t2.IsFaulted)
        {
            Console.WriteLine(t2.Exception.InnerException.Message);
        }
    }
}

输出:

first
second

try/catch 代码块外声明任务变量t1、t2,使他们可以在 try/catch 块内访问,在这里,使用了IsFaulted 属性,检查任务的状态,若IsFaulted 属性为 true ,则表示该任务出现异常,就可以使用 Task.Exception.InnerException 访问异常本身。

使用AggregateException信息

除了上述方式外,还有一种更好的获取所有任务的异常信息的方式,Task.WhenAll() 方法返回的结果其实也是一个 Task 对象,而 Task 有一个 Exception 属性,它的类型是 AggregateException,是 Exception的一个派生类,AggregateException 类有一个 InnerExceptions 属性(异常集合,包含 Task.WhenAll() 方法列表中所有异常任务的异常信息)。

有了这个属性则可以轻松遍历所有异常。如下代码:

public static async void StartTwoTasksParallelEx2()
{
    Task t3 = null;
    try
    {
        Task t1 = ThrowAfter(1000, "first");
        Task t2 = ThrowAfter(1000, "second");
        await (t3 = Task.WhenAll(t2, t1));

    }
    catch (Exception ex)
    {
        foreach (var item in t3.Exception.InnerExceptions)
        {
            Console.WriteLine("InnerException:" + item.Message);
        }
    }
}

输出:

InnerException:second
InnerException:first

总结

除了前面提到的异步方法异常处理的基本知识点,以下是一些进阶的异常处理技巧:

  • 在异步方法中,如果需要将异常传递给调用方,请不要直接抛出异常。相反,应该使用 throw 关键字将异常包装在一个 TaskValueTask 对象中,并将其返回给调用方。这可以避免在异步操作中丢失异常信息。

  • 如果需要在异步方法中处理多个异常,可以使用 catch 块来捕获不同类型的异常,并根据需要执行不同的处理操作。还可以使用 finally 块来执行清理操作,例如释放资源或恢复状态。

  • 如果需要在异步方法中执行一些异步操作,并且这些操作都必须成功才能继续执行下一步操作,那么可以使用 Task.WhenAll 方法来等待所有异步操作完成。如果任何一个异步操作失败,WhenAll 方法将返回一个 AggregateException 对象,其中包含所有失败的异常。

  • 如果需要在异步方法中执行多个异步操作,并且这些操作中的任何一个失败都将导致整个操作失败,那么可以使用 Task.WhenAny 方法来等待第一个异步操作完成。如果第一个操作失败,WhenAny 方法将返回一个 AggregateException 对象,其中包含第一个失败的异常。

  • 如果需要在异步方法中进行错误处理并且希望能够获取更多有关异常的信息,可以使用 ExceptionDispatchInfo 类。这个类可以捕获异常并将其存储在一个对象中,然后在需要时重新抛出异常。这可以帮助在异步操作中保留异常信息,并将其传递给调用方。

总之,在异步方法中处理异常时,需要注意一些细节和技巧,例如正确处理异常、捕获多个异常、等待多个异步操作、以及使用 ExceptionDispatchInfo 类来捕获异常。掌握这些处理技巧可以帮助编写更可靠、更健壮的异步代码。文章来源地址https://www.toymoban.com/news/detail-617086.html

到了这里,关于并发编程 --- 异步方法的异常处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python异步编程之web框架 异步vs同步 Redis并发对比

    主题: 比较异步框架和同步框架在RedisIO操作的性能差异 python版本 :python 3.8 数据库 :redis 5.0.7 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz flask是python中轻量级web框架,特点是灵

    2024年02月10日
    浏览(32)
  • 【文末送书】Python高并发编程:探索异步IO和多线程并发

    欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。搜索关注公粽号 《机器和智能》 发送“刷题宝

    2024年02月15日
    浏览(32)
  • 并发编程 | 从Future到CompletableFuture - 简化 Java 中的异步编程

    在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任务,显著提升了代码的可读性和简洁性。 在这篇

    2024年02月13日
    浏览(34)
  • Python异步编程高并发执行爬虫采集,用回调函数解析响应

    异步技术是Python编程中对提升性能非常重要的一项技术。在实际应用,经常面临对外发送网络请求,调用外部接口,或者不断更新数据库或文件等操作。 这这些操作,通常90%以上时间是在等待,如通过REST, gRPC向服务器发送请求,通常可能等待几十毫秒至几秒,甚至更长。如

    2024年02月08日
    浏览(39)
  • Python异步编程之web框架 异步vs同步 数据库IO任务并发支持对比

    主题: 比较异步框架和同步框架在数据库IO操作的性能差异 python版本 :python 3.8 数据库 :mysql 8.0.27 (docker部署) 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz python中操作数据库通常

    2024年02月08日
    浏览(40)
  • ARMv8-AArch64 的异常处理模型详解之异常处理详解(同步异常和异步异常的分析和处理)

    在文章ARMv8-AArch64 的异常处理模型详解之异常类型 Exception types中提到过,同步异常是处理器在执行指令时产生的异常,是一种精确的,可以具体定位到是哪条指令导致异常的产生。下面笔者将介绍三个用于定位并分析同步异常产生的寄存器。 在文章ARMv8-AArch64 的异常处理模型

    2024年03月27日
    浏览(33)
  • Vue公共loading升级版(处理并发异步差时响应)

    公共loading是项目系统中很常见的场景,处理方式也不外乎三个步骤: 1.通过全局状态管理定义状态值(vuex、pinia等)。 2.在程序主入口监听状态值变化,从而展示/隐藏laoding动画。 3.在请求和相应拦截器中变更状态值。 第一二步骤处理大同小异,但在第三步中,网上很多博文

    2024年02月05日
    浏览(37)
  • 【并发编程】线程池多线程异步去分页调用其他服务接口获取海量数据

    前段时间在做一个数据同步工具,其中一个服务的任务是调用A服务的接口,将数据库中指定数据请求过来,交给kafka去判断哪些数据是需要新增,哪些数据是需要修改的。 刚开始的设计思路是,,我创建多个服务同时去请求A服务的接口,每个服务都请求到全量数据,由于这些

    2024年02月13日
    浏览(31)
  • Java高并发系列: 使用wait - notify实现高效异步方法

    在项目开发中, 通常会有异步执行操作, 例如: 提交一个异步清空一系列数据库中ID = ${_id} 的记录, 这个时候通常的做法是主线程将任务添加到一个异步队列中, 后台维护一个线程不断地 循环 扫描这个队列, 如果有需要执行的任务, 则执行相应的逻辑. 如下图所示: 代码实现如下

    2024年02月09日
    浏览(42)
  • 【并发编程】自研数据同步工具的优化:创建线程池多线程异步去分页调用其他服务接口获取海量数据

    前段时间在做一个数据同步工具,其中一个服务的任务是调用A服务的接口,将数据库中指定数据请求过来,交给kafka去判断哪些数据是需要新增,哪些数据是需要修改的。 刚开始的设计思路是,,我创建多个服务同时去请求A服务的接口,每个服务都请求到全量数据,由于这些

    2024年02月12日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包