学习一下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
- Linux DNS之dig利器
- Docker系列教程08-Dockerfile实战
- VirtualBox和Vmware安装增强功能
- 事件委托与阻止冒泡
- Linux网络工具之Ping命令
- Linux DNS之nslookup命令
- 【LEETCODE】模拟面试-108-Convert Sorted Array to Binary Search Tree
- javascript单线程环境实现真正的setTimeout
- 利用pipework与OVS构建跨主机容器网络
- Docker系列教程07-Dockerfile指令详解
- Docker系列教程06-实战:修改Nginx首页
- prompt() 方法,弹框带输入框
- Docker系列教程05-容器常用命令
- 数据分析利器-NumPy
- 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 数组属性和方法
- PHP的JSON封装、转变及输出操作示例
- php 策略模式原理与应用深入理解
- 解决在Laravel 中处理OPTIONS请求的问题
- PHP使用观察者模式处理异常信息的方法详解
- php判断目录存在的简单方法
- 怎么优雅的使用 laravel 的 validator验证办法
- Thinkphp5.0框架的Db操作实例分析【连接、增删改查、链式操作等】
- php设计模式之适配器模式原理、用法及注意事项详解
- php基于Redis消息队列实现的消息推送的办法
- tp5框架无刷新分页实现方法分析
- PHP与SQL语句写一句话木马总结
- golang实现php里的serialize()和unserialize()序列和反序列办法详解
- php和html的区别点详细总结
- 详解在YII2框架中使用UEditor编辑器发布文章
- 在Laravel的Model层做数据缓存的实现