返回顶部
首页 > 资讯 > 后端开发 > Python >Python asyncio的示例分析
  • 656
分享到

Python asyncio的示例分析

2023-06-22 02:06:27 656人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

python asyncio的示例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们先从一个常见的Python编程错误开始说起,我已经见过非常多的程序员犯过这

python asyncio的示例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

我们先从一个常见的Python编程错误开始说起,我已经见过非常多的程序员犯过这种错误了:

def do_not_raise(user_defined_logic):    try:        user_defined_logic()    except:        logger.warning("User defined logic raises an exception", exc_info=True)        # ignore

这段代码的错误之处在哪里呢?

我们从Python的异常结构开始说起。Python中的异常基类有两个,最基础的是BaseException,第二个是Exception(继承BaseException)。这两者有什么区别呢?

Exception代表大部分我们经常会在业务逻辑中处理到的异常,也包括一部分运行出错例如NameErrorAttributeError等等。但是并不是所有的异常都是Exception类的子类,少数几个异常是继承于BaseException的:

  • GeneratorExit

  • SystemExit

  • KeyboardInterrupt

第一个代表生成器被close()方法关闭,第二个代表系统退出(例如使用sys.exit),第三个代表程序被Ctrl+C中断。之所以它们并不继承于Exception,是因为:它们一般情况下绝不应当被捕获,或者被捕获之后应当立即reraise(通过不带参数的raise语句)。

如果写出上面那样的语句,就可能会出现程序无法退出的情况:从外部发送SIGTERM信号到程序,触发了SystemExit,然而SystemExit被捕获然后忽略了,这样程序就没有正常退出,而是继续执行下去。像SystemExit、KeyboardInterrupt、GeneratorExit这样的异常,因为没有固定的抛出位置,所以如果乱捕获的话非常危险,很可能产生隐含的bug,而且测试中会很难发现。这就是为什么Python官方文档上会强调,如果使用无参数的except,一定要配合raise重新将异常抛出。而正确的忽略执行异常的方法应该是:

def do_not_raise(user_defined_logic):   try:       user_defined_logic()   except Exception:          ### <= Notice here ###       logger.warning("User defined logic raises an exception", exc_info=True)       # ignore

那么说了这么多,跟asyncio有什么联系呢?

asyncio当中,一个异步过程可以通过asyncio.Task作为一个独立执行的单元启动,这个Task对象有一个cancel()方法,可以将它从中途强制停止。类似的,异步生成器也可以通过aclose()方法强制结束。当一个异步过程或者异步生成器被从外部强制中止的时候,会从当前的await或者yield语句抛出asyncio.CancelledError

问题就出在这个CancelledError上!

asyncio也许是为了偷懒,也许是为了和concurrent一致,这个异常实际上是concurrent.futures.CancelledError。它的基类是Exception,而不是BaseException。要知道,在concurrent库当中,CancelledError是不会抛到已经开始了的子过程中的,它只会从future对象里抛出;而asyncio中,当使用了cancel()方法的时候,这个异常会从Task的当前堆栈位置抛出来。

这个事情就尴尬了,如果前面的do_not_raise是个异步方法,用 except Exception来捕获了用户自定义方法中的异常,那CancelledError也会被捕获到。结果就是CancelledError被错误地忽略掉,导致cancel()方法没有成功终止掉一个Task。

更尴尬的事情在于这个CancelledError的抛出机制。asyncio内部使用了Python的生成器和yield from机制,yield from可以自动代理异常,

为了说明这一点我们考虑下面的代码:

import tracebackimport asyncioasync def func1():    try:        return await func2()    except Exception:        traceback.print_exc()        raiseasync def func2():    try:        await asyncio.sleep(2)    except Exception:        traceback.print_exc()        raiseasync def func3():    t1 = asyncio.ensure_future(func1())    await asyncio.sleep(1)    t1.cancel()    try:        await t1    except CancelledError:        pass

t1.cancel()这里,会发生什么呢?实际上异常会从最内层的func2开始抛出,从func2抛出到func1,再到func3的await t1,所以可以看到两次traceback打印。

这就是异步方法中await的异常代理机制,它像同步调用一样,有完整的堆栈,并且异常从最内层抛出。这本身是一个很好的设计,很方便调试,但是一旦CancelledError抛出,你是无法确定它具体从哪条语句抛出的,这样在写异步逻辑的时候,实际上必须假设所有的await语句都有可能抛出CancelledError。如果在外面加上了前面的do_not_raise这样的机制,就会错误地忽略掉CancelledError

所以异步逻辑中的忽略异常必须写成:

async def do_not_raise(user_defined_coroutine):    try:        await user_defined_coroutine    except CancelledError:        raise    except Exception:        logger.warning("User defined logic raises an exception", exc_info=True)        # ignore

这样才能保证CancelledError不被错误捕获。

从这个结果上来看,CancelledError从一开始就不应该继承自Exception,它应该是一个BaseException,这样就可以减少很多异步编程中的错误。

并不是自己不调用cancel()就不会出现这样的问题。一些会触发cancel()过程的常见例子包括:

asyncio.wait_for在执行超时的时候会自动cancel内部的过程,这是一个很常用的实现超时逻辑的方法
aioHttphandler,如果没有处理完成之前用户就关闭了HTTP连接(比如强制点了浏览器的停止按钮),会对handler的异步过程调用cancel()
……
还有更尴尬的事情,许多时候我们不得不捕获CancelledError。刚才的一段代码,我故意没有提,读者们是否发现问题了呢?

t1.cancel()    try:        await t1    except CancelledError:        pass

在asyncio中,cancel()方法并不会立即结束一个异步Task,它只会抛出CancelledError,但是异步过程有机会使用except或者finally,在退出之前执行一些清理过程。这里的await的本意也是等待t1完全退出再继续。但是t1会抛出CancelledError,所以捕获这个异常,不让它再抛出。(而且如果不这么做,asyncio会打印一行warning,表示一个异步Task失败没有被处理)

那么问题就来了:如果func3()在执行到这里的时候,又被外部代码cancel()了呢?下面的except CancelledError就会变成问题,它会错误捕获外部的CancelledError。另外,t1也会再次被cancel一遍(没错,await一个Task的时候,如果await所在过程被cancel,Task也会被cancel,需要使用asyncio.shield来规避)

正确的写法应该是:

t1.cancel()    await asyncio.wait([t1])    try:        await t1    except CancelledError:        pass

asyncio.wait等待Task执行结束,但并不收集结果,因此内层的CancelledError不会在这里抛出来,而且如果此时取消func3,CancelledError并不会被忽略。第二个await t1时,t1可以保证已经结束,这里内部没有其他异步等待过程,因此CancelledError不会抛出在这里。也可以用t1.exception()之类代替。

关于Python asyncio的示例分析问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网Python频道了解更多相关知识。

--结束END--

本文标题: Python asyncio的示例分析

本文链接: https://lsjlt.com/news/301768.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python asyncio的示例分析
    Python asyncio的示例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们先从一个常见的Python编程错误开始说起,我已经见过非常多的程序员犯过这...
    99+
    2023-06-22
  • python中asyncio异步编程的示例分析
    这篇文章将为大家详细讲解有关python中asyncio异步编程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1.   想学asyncio,得先了解协程携程的意义:计算型的操...
    99+
    2023-06-14
  • Python +Selenium的示例分析
    本篇文章给大家分享的是有关Python +Selenium的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。下面,我们对比几大主流编程语言的代码量,以「Hello Wor...
    99+
    2023-06-05
  • python属性的示例分析
    这篇文章主要介绍了python属性的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。属性(property)##不推荐 class&n...
    99+
    2024-04-02
  • python中waitKey的示例分析
    这篇文章主要介绍了python中waitKey的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传...
    99+
    2023-06-14
  • python模块的示例分析
    小编给大家分享一下python模块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传...
    99+
    2023-06-14
  • python中flask的示例分析
    这篇文章主要介绍python中flask的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一.简介Flask是一个使用Python编写的轻量级Web应用框架。基于Werkzeug WSGI工具箱和Jinja2 ...
    99+
    2023-06-20
  • Python中LightGBM的示例分析
    这篇文章主要介绍Python中LightGBM的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、IntroductionLightGBM是扩展机器学习系统。是一款基于GBDT(梯度提升决策树)算法的分布梯度...
    99+
    2023-06-26
  • python中xkcd的示例分析
    这篇文章将为大家详细讲解有关python中xkcd的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。xkcd幽默是 Python 语言的一个关键特征,它是以英国喜剧小品剧Python飞行马戏团命名的...
    99+
    2023-06-27
  • python中zip的示例分析
    这篇文章主要介绍了python中zip的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。zip压轴出场的也是很棒的一个模块。你曾经遇到过需要从两个列表中形成字典吗?ke...
    99+
    2023-06-27
  • python页面解析的示例分析
    这篇文章主要介绍python页面解析的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!页面解析对于页面解析最强大的当然是正则表达式,这个对于不同网站不同的使用者都不一样,就不用过多的说明。其次就是解析库了,常用...
    99+
    2023-06-08
  • Python代码示例分析
    这篇文章主要介绍了Python代码示例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python代码示例分析文章都会有所收获,下面我们一起来看看吧。题目加粗,注意事项红色(...
    99+
    2024-04-02
  • python装饰器的示例分析
    这篇“python装饰器的示例分析”除了程序员外大部分人都不太理解,今天小编为了让大家更加理解“python装饰器的示例分析”,给大家总结了以下内容,具有一定借鉴价值,内容详细步骤清晰,细节处理妥当,希望大家通过这篇文章有所收获,下面让我们...
    99+
    2023-06-06
  • Python反斜杠的示例分析
    小编给大家分享一下Python反斜杠的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!首先我们写一段非常简单的Python代码,它的作用是把一个字段先转换为...
    99+
    2023-06-14
  • Python中算法的示例分析
    小编给大家分享一下Python中算法的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1. 算法的设计要求算法分析的主要目标是从运行时间和内存空间消耗等方面...
    99+
    2023-06-22
  • python graphviz画图的示例分析
    python graphviz画图的示例分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。前言:最近要开始做课设来着,题目是哈夫曼编码,我琢磨着有什么办法可以打印出一...
    99+
    2023-06-22
  • python中搜索的示例分析
    这篇文章将为大家详细讲解有关python中搜索的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1. 普通搜索搜索是指从元素集合中找到某个特定元素的算法过程。搜索过程通常返回 True 或 Fals...
    99+
    2023-06-22
  • Python默认值的示例分析
    这篇文章给大家分享的是有关Python默认值的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、使用注意使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够...
    99+
    2023-06-15
  • python中集合的示例分析
    这篇文章主要介绍python中集合的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、集合的基本信息集合:集合是无序的,集合中的元素是唯一的,集合一般用于元组或者列表中的元素去重。格式:set1 = set(...
    99+
    2023-06-15
  • Python多线程的示例分析
    这篇文章将为大家详细讲解有关Python多线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。线程相对进程来说是“轻量级”的,操作系统用较少的资源创建和管理线程。程序中的线程在相同的内存空间中执行...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作