第101天: Python asyncio

异步IO之asyncio

异步IO:当发起一个 IO 操作时,并不需要等待它的结束,程序可以去做其他事情,当这个 IO 操作结束时,会发起一个通知。

在 Python 中可以使用 asyncio 模块异步编程,用于协程、网络爬虫、同步等。

asyncio 中的概念

event_loop 事件循环

事件循环是 asyncio 应用的核心,管理所有的事件。

  1. 创建新的事件循环
asyncio.new_event_loop()
  1. 获取当前线程中正在执行的事件循环
asyncio.get_running_loop()
  1. 并发运行任务
asyncio.gather()
  1. 向指定的事件添加一个任务
asyncio.run_coroutine_threadsafe()
  1. 返回没有执行的事件
asyncio.all_tasks()

Future 对象

一个 Future 代表一个异步运算的结果,线程不安全。

Task 对象

Task 对象的作用是在运行某个任务的同时可以并发的运行其他任务

Task 对象可以使用 asyncio.create_task() 函数创建,也可以使用 loop.create_task() 和 asyncio.ensure_future() 函数创建,不建议实例化 Task对象

  1. 取消 Task 对象
cancel()
  1. Task 任务是否被取消
cancelled()
  1. Task 对象是否完成
done()
  1. 返回结果
result()
  • Task 对象被完成,则返回结果
  • Task 对象被取消,则引发 CancelledError 异常
  • Task 对象的结果不可用,则引发 InvalidStateError 异常
  1. 添加回调,任务完成时触发
add_done_callback(task)
  1. 所有任务列表
asyncio.all_tasks()
  1. 返回当前任务
asyncio.current_task()

运行协程

import asyncio
async def do_work(): print("Hello....") # 模拟阻塞1秒 await asyncio.sleep(1) print("world...")
coroutine = do_work()print(coroutine)
# 创建一个事件event_looploop = asyncio.get_event_loop()
# 将协程加入到event_loop中,并运行loop.run_until_complete(coroutine)

示例结果

<coroutine object do_work at 0x1108c50c8>Hello....# 这里会暂停1秒world...

在 Python 中使用 async def 定义一个协程( coroutine ),它并不能直接运行,需要加入到事件循环( event_loop )中

运行 Task

import asyncio

async def do_work(): print("这是一个Task例子....") # 模拟阻塞1秒 await asyncio.sleep(1) return "Task任务完成"
# 创建一个事件event_looploop = asyncio.get_event_loop()
# 创建一个tasktask = loop.create_task(do_work())# 第一次打印taskprint(task)
# 将task加入到event_loop中loop.run_until_complete(task)# 再次打印taskprint(task)print(task.result())

示例结果

<Task pending coro=<do_work() running at /Users/imeng/Documents/Interview/Python/asyncio_test.py:5>>这是一个Task例子....<Task finished coro=<do_work() done, defined at /Users/imeng/Documents/Interview/Python/asyncio_test.py:5> result='Task任务完成'>Task任务完成

使用 EventLoop 对象的 create_task 函数创建一个 Task 对象,在第一次打印 Task 对象时,状态为 pending,完成执行函数后的状态为 finished

Task 对象的 result() 函数可以获取 do_work() 函数的返回值

Task 任务回调

import asyncio
async def do_work(): print("这是一个Task例子....") # 模拟阻塞1秒 await asyncio.sleep(1) return "Task任务完成"
# 任务完成后的回调函数def callback(task): # 打印参数 print(task) # 打印返回的结果 print(task.result())
# 创建一个事件event_looploop = asyncio.get_event_loop()
# 创建一个tasktask = loop.create_task(do_work())task.add_done_callback(callback)
# 将task加入到event_loop中loop.run_until_complete(task)

示例结果

这是一个Task例子....<Task finished coro=<do_work() done, defined at /Users/imeng/Documents/Interview/Python/asyncio_test.py:5> result='Task任务完成'>Task任务完成

定义回调函数时必须有一个参数,参数和 Task 任务时同一个对象,使用 add_done_callback() 函数为 Task 任务添加一个完成后的回调函数

并发任务

import asyncioimport time
async def do_work(t): print("暂停" + str(t) + "秒") # 模拟阻塞1秒 await asyncio.sleep(t) return "暂停了" + str(t) + "秒"

# 任务完成后的回调函数def callback(future): # 打印返回的结果 print(future.result())

# 创建一个事件event_looploop = asyncio.get_event_loop()
tasks = []i = 0while i <= 4: task = loop.create_task(do_work(i)) task.add_done_callback(callback) tasks.append(task) i += 1;

# 计时now = lambda :time.time()start = now()# 将task加入到event_loop中loop.run_until_complete(asyncio.wait(tasks))
end = now()print("总共用时间:",end-start)

示例结果:

暂停0秒暂停1秒暂停2秒暂停3秒暂停4秒暂停了0秒暂停了1秒暂停了2秒暂停了3秒暂停了4秒总共用时间: 4.003800868988037

使用 asyncio.wait() 函数将 Task 任务列表添加到 event_loop 中,也可以使用 asyncio.gather() 函数

在示例中可以看出多个协程总共用时4秒多,如果是同步任务将需要花费10秒多,asyncio 实现了程序的并发

同一个回调

import asyncioimport functools

async def do_work(t): print("暂停" + str(t) + "秒") await asyncio.sleep(t) return "暂停了" + str(t) + "秒"

def callback(loop, gatheringFuture): print(gatheringFuture) print("多个Task任务完成后的回调") loop.stop()

loop = asyncio.get_event_loop()
gather = asyncio.gather(do_work(1), do_work(3))gather.add_done_callback(functools.partial(callback, loop))
loop.run_forever()

示例结果

暂停1秒暂停3秒<_GatheringFuture finished result=['暂停了1秒', '暂停了3秒']>多个Task任务完成后的回调

loop.run_forever() 函数 和 loop.run_until_complete() 函数 并不相同,run_until_complete() 函数在执行后事件循环被停止,run_forever() 函数在 Task 任务执行完成后事件循环并没有被终止,在回调函数 callback() 中使用 loop.stop() 函数将事件循环停止

总结

asyncio 在协程、网络爬虫等多种耗时操作时程序不再需要等待其他任务完成,节约大量的时间。

代码地址

示例代码:https://github.com/JustDoPython/python-100-day/tree/master/day-101

系列文章

第100天:UDP 编程
第99天:TCP 编程
第98天:图像库 PIL 实例—验证码去噪
第97天:图像库 PIL(二)
第96天:图像库 PIL(一)
第95天:StringIO & BytesIO
第94天:数据分析之 pandas 初步
第93天:文件读写
第92天:Python Matplotlib 进阶操作
第91天:Python matplotlib introduction
从 0 学习 Python 0 - 90 大合集总结
(0)

相关推荐

  • WebSocket

    目录 一.WebSocket -网络通信协议 1-1 简介 二.Websockets servers and clients in Python 2-0 connect 2-0-1 建立一对一短连接 ...

  • gevent:异步理论与实战

    gevent库中使用的最核心的是Greenlet-一种用C写的轻量级python模块.在任意时间,系统只能允许一个Greenlet处于运行状态.那怎么让程序高并发,从而实现程序高效运行呢? 这就是我们 ...

  • 用一个开源工具实现多线程 Python 程序的可视化 | Linux 中国

    原创 邀你一起成为开源贡献者 Linux中国   导读:VizTracer 可以跟踪并发的 Python 程序,以帮助记录.调试和剖析. 本文字数:4686,阅读时长大约:6分钟 https://li ...

  • 如何使用Python异步编程进行API调用 | 区块链研究实验室

    原创 链三丰 区块链研究实验室 今天 收录于话题 #Python1 #区块链技术33 #区块链44 #API1 #区块链应用30 本文中,将向大家介绍如何使用Python异步编程,以便您可以更快地进行 ...

  • 总算搞明白了!进程,线程,协程,生成器,迭代器搞的我脑子好乱!

    你是否曾经被迭代器,生成器,进程,线程,协程搞的脑子很乱? 而且剪不断,理还乱: 这不怪你,这是有历史原因.本文试图把东西都给理顺了. 一篇不行,咱们就再来一篇,使劲点赞. 两个问题,三种协程 先来看 ...

  • 理解 asyncio 来构建高性能 Python 网络程序

    Python 是一门上手快.优雅简洁的编程语言,其多范式.丰富的标准库和第三方库能够让编程人员把精力集中在逻辑和思维方法上,而不用去担心复杂语法.类型系统等外在因素,从而高效地达成自己的编程目标.Py ...

  • uvloop-一个快速的,替代asyncio内置的事件循环的python库

    uvloop是一个快速的,替代asyncio内置的事件循环的python库,uvloop在Cython中实现,并在内部使用libuv. 性能 uvloop使asyncio加快2-4倍. 上图显示了具有 ...

  • 名医名方第101期|苏前止咳汤

    张志明,甘肃省名中医. 组成:苏梗12克,射干12克,白前12克,紫菀12克,五味子6克,生姜3片. 功能:宣肺散寒,降气止咳. 主治:外感风寒,表证已解,咳嗽迁延不愈,证属寒郁肺气.肺失宣肃者.症见 ...

  • 国学经典名言、名句、格言,500句!(101~200)

    101.君子坦荡荡,小人长戚戚.(论语述而)102.疾风知劲草,板荡识诚臣.(唐太宗赠萧禹)103.博学之,审问之,慎思之,明辨之,笃行之.<礼记>104.不期乎骥骜吕氏春秋105.天作孽 ...

  • Python|二叉树叶子结点问题解决方法

    问题描述键盘输入一颗二叉树,求解其叶子结点个数.示例: 输入:4,2,6,1,3,5输出:3解决方案一棵树当中没有子结点(即度为0)的结点称为叶子结点,简称"叶子".当二叉树为空时 ...

  • 101岁松下的“中国速度”

    上海虹桥,国家会展中心外形酷似四叶草,规模大得惊人.这里不仅有成排的餐厅,随处可见的便利店,还有电影院.酒店,甚至救护车都可以在展馆内穿行. 11月5日到10日,第二届中国国际进口博览会在这里举行. ...

  • Python数据分析库有哪些?常见分类!

    众所周知,Python前景好.需求量大.薪资高.就业岗位多,除了基本的开发工作之外,还可以从事人工智能.数据分析.网络爬虫等岗位.那么说起数据分析,你知道Python常用数据分析库有哪些吗?我们一起来 ...

  • PyPy为什么能让Python比C还快?一文了解内在机制

    来自|机器之心 「如果想让代码运行得更快,您应该使用 PyPy.」 - Python 之父 Guido van Rossum 对于研究人员来说,迅速把想法代码化并查看其是否行得通至关重要.Python ...

  • 【Python爬虫】:使用高性能异步多进程爬虫获取豆瓣电影Top250

    在本篇博文当中,将会教会大家如何使用高性能爬虫,快速爬取并解析页面当中的信息.一般情况下,如果我们请求网页的次数太多,每次都要发出一次请求,进行串行执行的话,那么请求将会占用我们大量的时间,这样得不偿 ...