Python 核心技术与实战
景霄
Facebook 资深工程师
114324 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
开篇词 (1讲)
Python 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

20 | 揭秘 Python 协程

NGINX
协程如何实现回调函数
事件循环概念
协程的写法更加简洁清晰
协程和多线程的区别
协程版本的代码
同步版本的代码
使用协程实现
错误处理和超时取消
协程执行过程分析
asyncio.gather()等待所有任务结束
asyncio.create_task()创建任务
asyncio.run()触发运行
asyncio.create_task()创建任务
await关键字执行协程
async修饰词声明异步函数
使用协程写异步程序非常简单
async/await 语法糖
事件循环模型
多线程/多进程模型
思考题
总结
实战:豆瓣近日推荐电影爬虫
生产者消费者模型
解密协程运行时
任务(Task)
Python 3.7 提供了新的基于 asyncio 和 async/await 的方法
JavaScript 中的事件循环
协程是一种并发编程方式
生成器在 Python 2 中用来实现协程
Python 协程

该思维导图由 AI 生成,仅供参考

你好,我是景霄。
上一节课的最后,我们留下一个小小的悬念:生成器在 Python 2 中还扮演了一个重要角色,就是用来实现 Python 协程。
那么首先你要明白,什么是协程?
协程是实现并发编程的一种方式。一说并发,你肯定想到了多线程 / 多进程模型,没错,多线程 / 多进程,正是解决并发问题的经典模型之一。最初的互联网世界,多线程 / 多进程在服务器并发中,起到举足轻重的作用。
随着互联网的快速发展,你逐渐遇到了 C10K 瓶颈,也就是同时连接到服务器的客户达到了一万个。于是很多代码跑崩了,进程上下文切换占用了大量的资源,线程也顶不住如此巨大的压力,这时, NGINX 带着事件循环出来拯救世界了。
如果将多进程 / 多线程类比为起源于唐朝的藩镇割据,那么事件循环,就是宋朝加强的中央集权制。事件循环启动一个统一的调度器,让调度器来决定一个时刻去运行哪个任务,于是省却了多线程中启动线程、管理线程、同步锁等各种开销。同一时期的 NGINX,在高并发下能保持低资源低消耗高性能,相比 Apache 也支持更多的并发连接。
再到后来,出现了一个很有名的名词,叫做回调地狱(callback hell),手撸过 JavaScript 的朋友肯定知道我在说什么。我们大家惊喜地发现,这种工具完美地继承了事件循环的优越性,同时还能提供 async / await 语法糖,解决了执行性和可读性共存的难题。于是,协程逐渐被更多人发现并看好,也有越来越多的人尝试用 Node.js 做起了后端开发。(讲个笑话,JavaScript 是一门编程语言。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Python协程:并发编程的新利器 Python协程是一种强大的并发编程方式,通过async修饰词声明异步函数,以及使用await来调用协程实现异步执行。本文深入浅出地介绍了Python中协程的使用方法,从协程的背景和发展出发,引出了协程的重要性。通过一个简单的爬虫例子展示了使用协程进行并发化的优势。在Python 3.7以上版本中,使用协程编写异步程序变得非常简单。通过对比多线程的写法,展示了协程的清晰性和高效性。此外,文章还详细介绍了协程任务的创建、运行和取消,以及如何处理协程任务的超时和错误。最后,通过实例展示了使用协程实现生产者消费者模型和一个完整的协程爬虫。总之,本文通过实例详细介绍了Python中协程的基本概念和使用方法,适合读者快速了解和掌握协程的特点和使用技巧。 文章还提到了协程和多线程的区别,以及协程的写法更加简洁清晰,对于中小级别的并发需求已经毫无压力。最后,留下了一个思考题,即协程如何实现回调函数,鼓励读者留言讨论并分享文章。整体而言,本文是一篇介绍Python协程的实用指南,为读者提供了深入了解并发编程的机会。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Python 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(123)

  • 最新
  • 精选
  • Jingxiao
    置顶
    思考题答案: 在 python 3.7 及以上的版本中,我们对 task 对象调用 add_done_callback() 函数,即可绑定特定回调函数。回调函数接受一个 future 对象,可以通过 future.result() 来获取协程函数的返回值。 示例如下: import asyncio async def crawl_page(url): print('crawling {}'.format(url)) sleep_time = int(url.split('_')[-1]) await asyncio.sleep(sleep_time) return 'OK {}'.format(url) async def main(urls): tasks = [asyncio.create_task(crawl_page(url)) for url in urls] for task in tasks: task.add_done_callback(lambda future: print('result: ', future.result())) await asyncio.gather(*tasks) %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4'])) 输出: crawling url_1 crawling url_2 crawling url_3 crawling url_4 result: OK url_1 result: OK url_2 result: OK url_3 result: OK url_4 Wall time: 4 s
    2019-07-01
    42
  • Jingxiao
    置顶
    发现评论区好多朋友说无法运行,在这里统一解释下: 1. %time 是 jupyter notebook 自带的语法糖,用来统计一行命令的运行时间;如果你的运行时是纯粹的命令行 python,或者 pycharm,那么请把 %time 删掉,自己用传统的时间戳方法来记录时间也可以;或者使用 jupyter notebook 2. 我的本地解释器是 Anaconda Python 3.7.3,亲测 windows / ubuntu 均可正常运行,如无法执行可以试试 pip install nest-asyncio,依然无法解决请尝试安装 Anaconda Python 3. 这次代码因为使用了较新的 API,所以需要较新的版本号,但是朋友们依然出现了一些运行时问题,这里先表示下歉意;同时也想说明的是,在提问之前自己经过充分搜索,尝试后解决问题,带来的快感,和能力的提升,相应也是很大的,一门工程最需要的是 hands on dirty work(动手做脏活),才能让自己的能力得到本质的提升,加油!
    2019-06-25
    10
    76
  • Airnm.毁
    豆瓣那个发现requests.get(url).content/text返回都为空,然后打了下status_code发现是418,网上找418的解释,一般是网站反爬虫基础机制,需要加请求头模仿浏览器就可跳过,改为下面的样子就可通过:url = "https://movie.douban.com/cinema/later/beijing/" head={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36', 'Referer':'https://time.geekbang.org/column/article/101855', 'Connection':'keep-alive'} res = requests.get(url,headers=head)

    作者回复: 👍,可能是增加了反爬虫机制

    2020-04-18
    3
    8
  • jackstraw
    有点没明白,前面说任务创建后立马就开始执行了么?怎么后面在解密底层运行过程的时候,说任务创建后等待执行?到底是哪一个呀?

    作者回复: 原文是“任务创建后很快就会被调度执行”,并不是“立马就开始执行”;其次在解密底层运行时,main 函数也可以理解为一个正常的 task,要等这个 task 进入 await 的状态,才会调度下一个 task

    2020-01-14
    3
  • 长期规划
    老师,在最后那个协程例子中为何没用requests库呢?是因为它不支持协程吗

    作者回复: 协程使用的是 aiohttp 并发网络 io 库,因此就不需要 requests 了

    2019-12-20
    2
  • 一凡
    协程是单线程怎么理解?所有的协程都是吗

    作者回复: 对 单线程实现并发

    2020-06-18
    1
  • 苹果
    asyncio.run() cannot be called from a running event loop 这个问题是如何解决,

    作者回复: https://stackoverflow.com/questions/55409641/asyncio-run-cannot-be-called-from-a-running-event-loop

    2020-02-02
    1
  • 隰有荷
    老师,在如下代码中: async def worker_2(): print('worker_2 start') await asyncio.sleep(2) print('worker_2 done') 其中的await asyncio.sleep(2)是否可以理解为在切出当前程序,2秒后再继续执行print('worker_2 done')代码? 那么如果我有个耗时任务 def xxx(): ...,那么该如何用await asyncio来让这个xxx函数运行并切出当前程序呢?

    作者回复: 1. 基本是,但是注意是2秒后将当前断点加入协程池中等待调度。 2. 如果你只有这个耗时任务和当前任务(也就是总共两个任务),在当前任务调用 await 自动就切换到耗时任务了;但是当协程任务很多的时候,由于协程没有优先级设置,因此可能会出现耗时任务一直没有被分配资源的情况。实践中可以创建一个用于完成耗时任务的协程池,以限制耗时任务占用的总协程数量,于是就又回到了基于线程的并发模型中。

    2019-11-28
    1
  • 扶幽
    请问下有木有相关的书籍,来进行这块的学习呢!有些原理性的东西还是没办法深入理解,谢谢。

    作者回复: 最好的办法就是多实践。文中的原理性东西都已经讲了,你可以边实践,遇到问题再去google

    2019-10-12
    1
  • cotter
    受教了,第一次听说这个高级功能! 我在工作中遇到一个需要并发的问题,用python在后台并发执行shell ,并发数量用时间范围控制,要不停的改时间分多次串行,方法比较笨拙。协程可以简化我的代码。 老师,并发很多事件应该也是需要消耗很多资源,协程改如何控制并发数量?

    作者回复: 这个没有统一结论。需要根据硬件设置和具体线上的条件,进行测试莱选取最优的并发数量

    2019-06-24
    3
    1
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部