相比于多线程,协程或者叫微线程(coroutine),这样的方式效率更高,因为没有切换线程的开销,不存在变量冲突和进程锁等限制。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
简单例子
import threading
import asyncio
async def print_thread(th):
return str(th)[2:6]
async def hello():
print('Hello world! (%s)' % threading.currentThread())
r = await print_thread(threading.currentThread())
print('Hello %s' % r)
await asyncio.sleep(1)
print('Hello again! (%s)' % threading.currentThread())
loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
执行结果
Hello world! (<_MainThread(MainThread, started 12448)>)
Hello Main
Hello world! (<_MainThread(MainThread, started 12448)>)
Hello Main
(暂停1秒)
Hello again! (<_MainThread(MainThread, started 12448)>)
Hello again! (<_MainThread(MainThread, started 12448)>)
可以看到,使用的是一个进程。
async
标记把一个generator
标记为coroutine
类型- 从
asyncio
模块中直接获取一个EventLoop
的引用,然后把需要执行的协程扔到EventLoop
中执行,就实现了异步IO。 await
等同于yeild from
协程现阶段所存在的问题
await
后面跟的必须是Awaitable
或者是实现了__await__
的类,当前大部分的教程其实都只是讲了协程的原理,并且都是用syncio.sleep()
来作为演示。但是真正到使用环境的时候还是会束手束脚,如果没有对应的异步库,那么await
就无法使用。
那么有没有一种折中的办法呢?
还是有的,这篇文转讨论了如何用非异步库来调用协程。
给出的例子
import asyncio
import requests
from time import sleep
def run_async(callback):
def inner(func):
def wrapper(*args, **kwargs):
def __exec():
out = func(*args, **kwargs)
callback(out)
return out
return asyncio.get_event_loop().run_in_executor(None, __exec)
return wrapper
return inner
response = []
def _callback(*args):
print('callback: ', args)
response.append(args)
# Must provide a callback function, callback func will be executed after the func completes execution !!
@run_async(_callback)
def get(url):
return requests.get(url)
get("https://google.com")
print("Non blocking code ran !!")
while not response:
sleep(0.1)
print('waiting for response...')
print('get response: ', response)
## output:
#Non blocking code ran !!
#waiting for response...
#waiting for response...
#waiting for response...
#waiting for response...
#waiting for response...
#callback: (<Response [200]>,)
#waiting for response...
#get response: [(<Response [200]>,)]
其中loop.run_in_executor(None, func, *args)
可以将正常函数转化为asyncio.Future
对象,
一个 Future 代表一个异步运算的最终结果。线程不安全。
Future 是一个 awaitable 对象。协程可以等待 Future 对象直到它们有结果或异常集合或被取消。
异步库
- aiomysql 数据库操作
- aiohttp http请求
- aiofile 文件读写
- netdev 远程连接
- sqlalchemy[asyncio] 关系型数据库
异步测试
安装pytest-asyncio
@pytest.mark.asyncio
async def test_main():
...
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 365433079@qq.com