学习一下Python3的协程
背景
Python3推出好久了,其中的协程特性,一直没有时间来学习,这次跟着官方文档一起了解一下。
Python版本:3.6.6
Hello World
import asyncio
async def hello_world():
print("Hello World!")
loop = asyncio.get_event_loop()
loop.run_until_complete(hello_world())
loop.close()
这里其实跟我们正常执行函数,没有很大的区别。
根据官方的介绍,这里的协程是通过一个task去做的,因此这里需要定义一个loop
,这个loop
会监听传入函数的执行状态。执行完毕之后,要把这个loop
给关掉。
其实Python3.8
改版成asyncio.run()
,这种方式其实是更友好的,基本上不感知协程的实现方式了。
获取异步的结果
import asyncio
async def slow_operation(future):
await asyncio.sleep(1)
future.set_result('Future is done!')
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()
可以看到,这里实际上是通过定义了一个Future
来实现的,在异步函数执行完毕之后,通过set_result
方法来设置结果。在外层就可以拿到这个结果。这种方式,可以按同步的思维来写异步的程序,并且获取返回结果。
回调
import asyncio
async def slow_operation():
await asyncio.sleep(1)
return 'Future is done!'
def got_result(future):
print(future.result())
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(slow_operation())
task.add_done_callback(got_result)
loop.run_until_complete(task)
loop.close()
这里的实例是我拿官方的实例修改的,更方便理解。这里的回调,会扔到一个Future
对象里面。通过这个对象的方法拿到结果。
多任务执行
import asyncio
import time
async def factorial(name, number):
f = 1
for i in range(2, number+1):
print("Task %s: Compute factorial(%s)..." % (name, i))
await asyncio.sleep(1)
f *= i
print("Task %s: factorial(%s) = %s" % (name, number, f))
loop = asyncio.get_event_loop()
print(time.time())
loop.run_until_complete(asyncio.gather(
factorial("A", 2),
factorial("B", 2),
factorial("C", 2),
))
print(time.time())
loop.close()
这里引入了一个gather
方法。同构这个方法,可以同时执行多个任务。
上面的代码我把官方的内容改了一下。执行结果可以看到。这三个任务实际上是同时执行的。整个函数执行耗时也只有1s。也就是实现了并发。
官方文档这里的实例,实际上是为了说明,loop会等待所有的任务执行完毕才结束。
A task is automatically scheduled for execution when it is created. The event loop stops when all tasks are done.
一些小细节
在看这个内容的时候,我一直在想,使用time.sleep(1)
和await asyncio.sleep(1)
有什么区别。
如果使用await的,协程遇到这部分就会切换上下文,如果不实用await的,就会一直阻塞。也就意味着,如果不实用await。。。并发就是个伪命题。
把上面的多任务执行实例中的await asyncio.sleep(1)
改成time.sleep(1)
。你会发现,这三个任务是串行的。
参考
https://hackernoon.com/threaded-asynchronous-magic-and-how-to-wield-it-bba9ed602c32
https://docs.python.org/zh-cn/3.6/library/asyncio-task.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- seaborn数据总体分布的可视化策略
- 配置 Apache 服务器禁止所有非法域名 访问自己的服务器
- Ubuntu16.04源码安装Mininet
- Kotlin基础学习之Deprecated与Suppress注解使用
- Centos 7下利用crontab定时执行任务详解
- 树莓派无线上网时无屏幕下发现树莓派IP的方法
- Ubuntu18.04安装opencv 3.2.0的解决方法
- Android MVP BaseFragment 通用式封装的实现
- 腾讯云服务器Centos挂载数据盘的方法
- CentOS 8.0.1905 安装 ZABBIX4.4版本 (已验证)
- seaborn分类变量的汇总展示
- Linux查看PCIe版本及速率的方法
- android自定义滚轴选择器
- 虚拟机安装Linux rhel7.3操作系统(具体步骤)
- linux系统安装zookeeper 服务的方法