Python Asyncio 教程

Python 3.8 Asyncio - Asynchronous I/O 异步教程

本文适用于python 3.8
3.8版本更新去掉了几个函数的loop参数,以及一些函数的修改增强了可读性。
这是我的学习笔记,如有错误请多加指正。

天勤量化核心用的是asyncio,可以是金融行业一个很好的应用。我认为asyncio的优势是简化异步回调。将一个异步的架构转化成同步。代码量可以减少,更好想一些。 而且python本身有GIL的原因,本质是一个单线程的。 用多线程的思路写python没有意义。 我对于asyncio的形象理解是:如果你跑得足够快,一个人所有事情都可以飞速搞定,不需要等待。比如闪电侠做饭:买菜,烧水,切菜,炒菜,端菜都可以不间断的安排上。 多线程就好比你安排好几个人同时去买菜,烧水,切菜,炒菜,端菜。

还有一个协程的应用是pysimpleGUI。虽然pysimgpleGUI没有用到asyncio,思想是协程的思想,即用event loop替代callback function。简化了GUI创作的代码量。

关键概念

  • 同步和异步
  • async/await
  • 协程对象,协程函数
  • Awaitables: coroutinue, task, future
  • Event Loop

什么是asyncio?

Asyncio,即Asynchronous I/O是python一个用来处理并发(concurrent)事件的包,通过asnyc/await来表达, 是目前很多python异步架构的基础,多用作高性能网络处理方面,很容易解决了IO-bound的问题。

什么是协程 Coroutine?

协程是一种用户态的轻量级线程,不同于线程。 每一个协程可以暂时挂起以便其他协程开始工作。 当一个协程在等待网络请求时,我们可以把他挂起然后去做其他的事情。

协程系统需要具备管理何时挂起不同的协程以及运行需要的协程。 这个架构多半是通过event loop实现的。

协程函数(coroutine function)是通过async/await标识的特殊函数。
协程对象(coroutine object)是协程函数返回的对象object。
协程函数区别于普通函数,不能直接运行,而需要通过asyncio封装的函数运行。协程的异步是通过event loop实现的。 协程函数可以嵌套。

import asyncio
async def inner_coro(): # 定义
    return "Hello World"

async def main():
    message = await inner_coro()
    print(message)

result = asyncio.run(main()) # main()本身是一个协程
print(result)

运行协程的三种基本方式

  • async.run()
  • async.create_task()
  • async.gather()

1. 用asyncio.runners 运行最简单的协程函数:

asyncio.run(coro): 作为 top-level entry point "main()" function。封装了生成event loop,结果获取,结束时自己关闭event loop。 如果当前线程有event loop运行,则不能运行。

import asyncio
import time
async def main():
    print("start doing some work.")
    await asyncio.sleep(5)
    print("work is done.")
    return 0

start = time.time()
result = asyncio.run(main())
print(result)
now = time.time()
print("Run Time:", now - start)

2. asyncio.create_task(coro) -> Task

通过asyncio.create_task计划协程任务。
在调用main()之前,先把Tasks建好,然后一起运行会达到协程的效果。

对比两个不同的写法:

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    return what

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    start = time.time()
    await task1
    await task2

    now = time.time()
    print("Time Consumed: ", now - start)

asyncio.run(main()) # 总共需要2秒
import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    return what

async def main():
    start = time.time()
    await say_after(1,"hello")
    await say_after(2,"world")

    now = time.time()
    print("Time Consumed: ", now - start)

asyncio.run(main()) # 总共需要3秒

第二个写法需要三秒的原因是因为程序按顺序执行了say_after(1,"hello")和say_after(2,"world")。没有达到并发的效果。

3. asyncio.gather(*aws) -> results

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    return what

async def main():
    start = time.time()
    task1 = say_after(1,"hello")
    task2 = say_after(2,"world")
    message = await asyncio.gather(task1, task2)
    print(message)
    now = time.time()
    print("Time Consumed: ", now - start)


asyncio.run(main()) # 总共需要2秒

三种可以被挂起的对象 Awaitable objects

  • coroutines
  • Tasks
  • Futures

这三种object都可以被挂起。
协程是可挂起的。挂起后的协程就是协程的嵌套。

1. Coroutines

见上文

2. Tasks

Tasks are used to schedule coroutines concurrently. Tasks的作用是计划安排协程。

3. Futures

A Future is a special low-level awaitable object that represents an eventual result of an asynchronous operation.
Future的作用是用来绑定回调。通常我们不需要建立Future。

import time
import asyncio

now = lambda : time.time()

async def do_some_work(x):
    print('Waiting: ', x)
    return 'Done after {}s'.format(x)

def callback(future):
    print('Callback: ', future.result())

start = now()

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
loop.run_until_complete(task)

print('TIME: ', now() - start)



用functools定义回调函数参数

def callback(t, future):
    print('Callback:', t, future.result())

task.add_done_callback(functools.partial(callback, 2))

常用函数

asyncio.sleep(delay, result = None, *)

asyncio.gather(*aws, loop = None, return_exceptions = False)->aw

asyncio.shield(aw,*)

asyncio.wait_for(aw, timeout, *)
Cancel the futures when a timeout occurs.

asyncio.as_completed(aws,*,timeout = None)
Run awaitable objects in the aws set concurrently. Return an iterator of coroutines.

Scheduling from other threads

asyncio.current_task(loop=None)
If loop is None,get the current loop.

asyncio.all_tasks(loop = None)

References

https://docs.python.org/3/library/asyncio-task.html
https://faculty.ai/blog/a-guide-to-using-asyncio/

进程和协程:https://www.cnblogs.com/lxmhhy/p/6041001.html

https://www.jianshu.com/p/b5e347b3a17c

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353