python 异步IO

  1. 简单例子
  2. 协程现阶段所存在的问题
  3. 异步库
  4. 异步测试

相比于多线程,协程或者叫微线程(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)>)

可以看到,使用的是一个进程。

  1. async标记把一个generator标记为coroutine类型
  2. asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。
  3. 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