Python中协程 asyncio模块 概念 asyncio 是用来编写 并发 代码的库,使用 async/await 语法。 asyncio 被用作多个提供高性能 Python 异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。 asyncio 往往是构建 IO 密集型和高层级 结构化 网络代码的最佳选择。
作用
提供高层级API
并发地运行 Python 协程并对其执行过程实现完全控制
执行 网络 IO 和 IPC
控制 子进程
通过 队列 实现分布式任务
同步 并发代码
提供低层级API
创建和管理 事件循环,以提供异步 API 用于 网络化, 运行 子进程,处理 OS 信号 等等
使用 transports 实现高效率协议
通过 async/await 语法 桥接 基于回调的库和代码
asyncio模块之协程 协程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import asyncioasync def main () : print('hello' ) await asyncio.sleep(1 ) print('world' ) asyncio.run(main()) hello world import asyncioimport timeasync def say_after (delay, what) : await asyncio.sleep(delay) print(what) async def main () : print(f"started at {time.strftime('%X' )} " ) await say_after(1 , 'hello' ) await say_after(2 , 'world' ) print(f"finished at {time.strftime('%X' )} " ) asyncio.run(main()) started at 17 :13 :52 hello world finished at 17 :13 :55 async def main () : task1 = asyncio.create_task( say_after(1 , 'hello' )) task2 = asyncio.create_task( say_after(2 , 'world' )) print(f"started at {time.strftime('%X' )} " ) await task1 await task2 print(f"finished at {time.strftime('%X' )} " ) started at 17 :14 :32 hello world finished at 17 :14 :34
可等待对象
并发协程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import asyncioasync def factorial (name, number) : f = 1 for i in range(2 , number + 1 ): print(f"Task {name} : Compute factorial({i} )..." ) await asyncio.sleep(1 ) f *= i print(f"Task {name} : factorial({number} ) = {f} " ) async def main () : await asyncio.gather( factorial("A" , 2 ), factorial("B" , 3 ), factorial("C" , 4 ), ) asyncio.run(main()) Task A: Compute factorial(2 )... Task B: Compute factorial(2 )... Task C: Compute factorial(2 )... Task A: factorial(2 ) = 2 Task B: Compute factorial(3 )... Task C: Compute factorial(3 )... Task B: factorial(3 ) = 6 Task C: Compute factorial(4 )... Task C: factorial(4 ) = 24
超时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async def eternity () : await asyncio.sleep(3600 ) print('yay!' ) async def main () : try : await asyncio.wait_for(eternity(), timeout=1.0 ) except asyncio.TimeoutError: print('timeout!' ) asyncio.run(main()) timeout!
内省
语法 asyncio.current_task(loop=None):返回当前运行的 Task 实例,如果没有正在运行的任务则返回 None。 asyncio.all_tasks(loop=None):返回事件循环所运行的未完成的 Task 对象的集合。
Task 对象
概念 一个与 Future 类似 的对象,可运行 Python 协程。非线程安全。 Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。
支持协程的模块Greenlet/Gevent Greenlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pip install greenlet from greenlet import greenletdef foo (x) : print('{} at foo_1' .format(x)) g2.switch('Elijah' ) print('{} at foo_2' .format(x)) g2.switch() def bar (y) : print('{} at bar_1' .format(y)) g1.switch() print('{} at bar_2' .format(y)) g1 = greenlet(foo) g2 = greenlet(bar) g1.switch('yang' )
Gevent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pip install gevent import geventdef foo (x) : print('{} at foo_1' .format(x)) gevent.sleep(2 ) print('{} at foo_2' .format(x)) def bar (y) : print('{} at bar_1' .format(y)) gevent.sleep(1 ) print('{} at bar_2' .format(y)) g1 = gevent.spawn(foo, 'yang' ) g2 = gevent.spawn(bar, 'Elijah' ) gevent.joinall([g1,g2])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 from gevent import monkey;monkey.patch_all()from socket import *import geventdef server (server_ip,port) : s=socket(AF_INET,SOCK_STREAM) s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1 ) s.bind((server_ip,port)) s.listen(5 ) while True : conn,addr=s.accept() gevent.spawn(talk,conn,addr) def talk (conn,addr) : try : while True : res=conn.recv(1024 ) print('client %s:%s msg: %s' %(addr[0 ],addr[1 ],res)) conn.send(res.upper()) except Exception as e: print(e) finally : conn.close() if __name__ == '__main__' : server('127.0.0.1' ,8080 ) from socket import *client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1' ,8080 )) while True : msg=input('>>: ' ).strip() if not msg:continue client.send(msg.encode('utf-8' )) msg=client.recv(1024 ) print(msg.decode('utf-8' )) from threading import Threadfrom socket import *import threadingdef client (server_ip,port) : c=socket(AF_INET,SOCK_STREAM) c.connect((server_ip,port)) count=0 while True : c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8' )) msg=c.recv(1024 ) print(msg.decode('utf-8' )) count+=1 if __name__ == '__main__' : for i in range(500 ): t=Thread(target=client,args=('127.0.0.1' ,8080 )) t.start()
猴子补丁 名字的由来
说法一 这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。
说法二 由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。
作用 在程序运行是,将原本的模块功能替换成另外模块的功能