python并发编程之asyncio协程(三)
协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈;协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快、开销更小、效率更高,在有多IO操作的业务中能极大提高效率。
系列文章
asyncio模块创建协程
asyncio在python3.4后被内置在python中,使得python的协程创建变得更加方便。
import asyncio
import os
# async 关键字定义一个协程
async def target_func1():
print('the func start')
print(os.getpid())
print('the func end')
def run():
# 创建一个协程对象
coroutine = target_func1()
# 创建一个事件循环
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine) # 将协程对象添加到事件循环,运行直到结束
print(os.getpid())
loop.close() # 关闭事件循环
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
# 创建一个协程对象
coroutine = target_func1(loop)
loop.create_task(coroutine) # 创建一个任务并添加到事件循环中
loop.run_forever() # 开启无限循环,需要在异步函数中调用stop()使停止
loop.close()
if __name__ == '__main__':
run()
# 结果
the func start
4876
the func end
4876
以上可知,所有的代码段都是在一个进程的单线程中执行。
asyncio模块分析
- coroutine
被async修饰的函数调用后会生成协程函数,可以通过send唤醒执行。
async def target_func1():
print('the func start')
print(os.getpid())
print('the func end')
coroutine = target_func1()
try:
coroutine.send(None) # 唤醒协程
except StopIteration:
print('xx')
coroutine.close() # 关闭
- async
async关键字可以定义一个协程对象,被async修饰的函数变成了一个协程对象而不是一个普通的函数。
async def target_func1():
pass
coroutine = target_func1()
print(coroutine)
- await
await用于控制事件的执行顺序,它只能在异步函数中使用,即被async关键字定义的协程函数,否则报错。当执行到await时,当前协程挂起,转而去执行await后面的协程,完毕后再回到当前协程继续往下。
# async 关键字定义一个协程
async def target_func1():
print('the func start')
x = await target_func2() # 当前协程挂起
print(x)
print('the func end')
return 1
async def target_func2():
"""
目标函数2
:return:
"""
time.sleep(2)
print('the func end2')
return 0
- 主要方法
asyncio.get_event_loop():创建一个事件循环,所有的异步函数都需要在事件循环中运行;
asyncio.ensure_future():创建一个任务
asyncio.gather(*fs):添加并行任务
asyncio.wait(fs):添加并行任务,可以是列表
loop.run_until_complete(func):添加协程函数同时启动阻塞直到结束
loop.run_forever():运行事件无限循环,直到stop被调用
loop.create_task():创建一个任务并添加到循环
loop.close():关闭循环
loop.time():循环开始后到当下的时间
loop.stop():停止循环
loop.is_closed() # 判断循环是否关闭
loop.create_future():创建一个future对象,推荐使用这个函数而不要直接创建future实例
loop.call_soon() # 设置回调函数,不能接受返回的参数,需要用到future对象,立即回调
loop.call_soon_threadsafe() # 线程安全的对象
loop.call_later() # 异步返回后开始算起,延迟回调
loop.call_at() # 循环开始多少s回调
loop.call_exception_handler() # 错误处理
- 主要的类
Future:主要用来保存任务的状态;
Task:Future的子类,扩展了Future的功能;
# Future
from asyncio import Future
# future = Future()
# future.result() # 获取任务的结果
# future.remove_done_callback(fn) # 删除所有的回调函数并返回个数
# future.set_result('result') # 设置任务的结果,必须在result()之前执行,否则报错
# future.exception() # 获取任务的错误信息
# future.set_exception('bad') # 设置任务的错误信息
# future.add_done_callback('fn') # 添加回调函数
# Task
current_task():返回循环当前的任务,类方法
all_tasks():返回事件循环所有的任务
get_stack():获取其他协程的堆栈列表
print_stack:输出其他协程的堆栈列表
cancel:取消任务
实例
- 添加多个任务到事件循环
async def target_func3(name):
"""
:return:
"""
await asyncio.sleep(1)
print(name)
return 0
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
x = loop.run_until_complete(asyncio.gather(target_func3('A'),target_func3('B'),target_func3('C'),))
print(x) # 等待返回结果,一个列表,按照事件添加的顺序,但是计算的顺序是不定的
loop.close()
if __name__ == '__main__':
run1()
- 使用run_forever启动循环获取异步计算结果
run_forever()不能直接得到异步函数的返回结果,需要使用Future类来作为第三方保存结果,同时设置回调函数;
from asyncio import Future
from functools import partial
async def target_func0(name, future):
"""
目标函数2
:return:
"""
time.sleep(1)
print(name)
future.set_result(name) # 设置返回结果
def got_result(loop, future):
print(future.result()) # 处理结果
loop.stop() # 循环停止
def run():
loop = asyncio.get_event_loop()
future = Future(loop=loop)
res = asyncio.ensure_future(target_func0('A', future)) # 生成一个Task任务
print(res)
future.add_done_callback(partial(got_result, loop)) # 回调函数默认只能有一个参数future,必须使用偏函数
# print(future.result()) # future上下文必须先调用future.set_result。
loop.run_forever()
loop.close()
if __name__ == '__main__':
run()
- 链协程
协程里调用等待另外的协程完成后才能返回。
import asyncio
import time
# async 关键字定义一个协程
async def target_func1():
print('the func start')
x = await target_func2() # 等待协程完成,控制执行顺序
print(x)
print('the func end')
return 1
async def target_func2():
"""
目标函数2
:return:
"""
time.sleep(2)
print('the func end2')
return 0
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
x = loop.run_until_complete(target_func1())
print(x)
loop.close()
if __name__ == '__main__':
run()
- 普通回调实例
import asyncio
import time
from functools import partial
# async 关键字定义一个协程
async def target_func1():
print('the func end')
return 1
def get_res(loop):
print('xxxx')
loop.stop()
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
loop.create_task(target_func1())
# loop.call_soon(partial(get_res, loop)) # 设置回调函数,不能接受返回的参数,需要用到future对象
# loop.call_soon_threadsafe() # 线程安全的对象
# loop.call_later(delay=5, callback=partial(get_res, loop)) # 异步返回后开始算起,延迟5秒回调
# loop.call_at(when=8000,callback=partial(get_res, loop)) # 循环开始第8秒回调
# loop.call_exception_handler() # 错误处理
loop.run_forever()
loop.close()
if __name__ == '__main__':
run1()
本地IO和网络IO的异步使用
使用协程的目的是在系统发生io阻塞的时候,可以交出CUP的控制权,让其去执行其他的任务。实际使用时一般的场景有本地IO和网络IO。
- 网络IO
# 使用asyncio+aiohttp,如果想异步化,网络请求需要抛弃requests包
import asyncio
import time
from aiohttp import ClientSession
async def target2():
print('start2')
async with ClientSession() as session:
async with session.get(url='http://www.baidu.com') as rsp:
data = await rsp.read()
print('end2')
return data
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
tasks = [target2() for i in range(100)]
ts = asyncio.gather(*tasks)
t = time.time()
loop.run_until_complete(ts)
print(time.time()-t)
loop.close()
if __name__ == '__main__':
run1()
- 本地io
核心思想:将文件读写的while循环换成事件循环。
可参考:https://github.com/lyyyuna/script_collection/blob/master/aysncfile/asyncfile.py
协程Queue
asyncio模块也有自己的queue实现生产消费模式,只要有三种队列:Queue(先进先出),PriorityQueue(优先级队列),LifoQueue(栈),但是Queue不是线程安全的类,也就是说在多进程或多线程的情况下不要使用这个队列。
import asyncio
import time
from asyncio import Queue
# async 关键字定义一个协程
async def target_func1(q:Queue):
for i in range(100):
await q.put(i)
async def target_func2(q:Queue):
for i in range(100):
x = await q.get()
print(x)
def run1():
# 创建一个事件循环
loop = asyncio.get_event_loop()
q = Queue(100)
task = asyncio.gather(target_func1(q), target_func2(q))
loop.run_until_complete(task)
loop.close()
if __name__ == '__main__':
run1()
Queue的get(),join(),put()方法返回的都是协程,需要使用await关键字。
参考
python并发编程之asyncio协程(三)的更多相关文章
- python并发编程之gevent协程(四)
协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...
- Python核心技术与实战——十八|Python并发编程之Asyncio
我们在上一章学习了Python并发编程的一种实现方法——多线程.今天,我们趁热打铁,看看Python并发编程的另一种实现方式——Asyncio.和前面协程的那章不太一样,这节课我们更加注重原理的理解. ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- python并发编程之multiprocessing进程(二)
python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...
- python并发编程之threading线程(一)
进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...
- python异步编程之asyncio
python异步编程之asyncio 前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...
- python并发编程之线程/协程
python并发编程之线程/协程 part 4: 异步阻塞例子与生产者消费者模型 同步阻塞 调用函数必须等待结果\cpu没工作input sleep recv accept connect get 同 ...
- Python核心技术与实战——十七|Python并发编程之Futures
不论是哪一种语言,并发编程都是一项非常重要的技巧.比如我们上一章用的爬虫,就被广泛用在工业的各个领域.我们每天在各个网站.App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的. 正确并合 ...
- Python并发编程——多线程与协程
Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...
随机推荐
- Vue 取出记录数后,页面显示刚开始显示部分,点击更多显示全部
实例的实现,是使用computed计算属性,还有对数组使用.slice函数,不改变原数据对象. <div id="app"> <ul> <li v-f ...
- CTSC2012-Cheat
题意 给出一些母01串,多次询问,每次询问一个01串,问一个最大的\(L\),使得可以在询问串中选出若干个不相交的,长度大于等于\(L\)的子串,这些子串都在母串中出现过,且子串的长度和大于等于询问串 ...
- bzoj1031-字符加密
环的问题,经典方法倍长串,求出后缀数组,扫一次sa,如果sa[i]小于等于n,那么就输出这个字符串结尾的位置(即s[sa[i]+n-1]). 代码 #include<cstdio> #in ...
- BZOJ 2007 海拔(平面图最小割转对偶图最短路)
首先注意到,把一个点的海拔定为>1的数是毫无意义的.实际上,可以转化为把这些点的海拔要么定为0,要么定为1. 其次,如果一个点周围的点的海拔没有和它相同的,那么这个点的海拔也是可以优化的,即把这 ...
- 【入门向】使用 MetaHook Plus 绘制 HUD
MetaHook Plus 是一个GoldSrc引擎(就是的Half-Life.CS1.6的引擎)的客户端插件平台,它可以加载我们自己开发的DLL插件. 首先你需要安装一个 Visual Studio ...
- AngularJS中$watch
$watch在digest执行时,如果watch观察的value与上一次执行时不一样时,就会被触发.angularjs内部的watch实现了页面随model的及时更新.$watch 方法在用的时候主要 ...
- [十二]SpringBoot 之 servlet
Web开发使用 Controller 基本上可以完成大部分需求,但是我们还可能会用到 Servlet.Filter.Listener.Interceptor 等等. 当使用spring-Boot时,嵌 ...
- c++字符串排序
在主函数中输入10个等长的字符串,用另一函数对它们排序.然后在主函数输出这10个已排好序的字符串. 用两种方法完成. 方法一:用二维数组做函数参数: 方法二:用指向一维数组的指针做函数参数. 方法一: ...
- 解题:HNOI 2012 永无乡
题面 并查集维护连通性,然后暴力启发式合并就完了,记得合并时边DFS边清空数组 #include<cstdio> #include<cstring> #include<a ...
- [学习笔记]矩阵乘法及其优化dp
1.定义: $c[i][j]=\sum a[i][k]\times b[k][j]$ 所以矩阵乘法有条件,(n*m)*(m*p)=n*p 即第一个矩阵的列数等于第二个矩阵的行数,否则没有意义. 2.结 ...