在说明asyncio的基本用法之前,先来说明一下异步程式设计的几项基本概念:
事件迴圈(event loop)事件(event)回调函数(callback)先来思考一下为什么异步程式设计为什么要存在:
有多个任务需要同时执行多个任务可以同时进行才符合一般人使用电脑的需求,假设我今天打开电脑,我想要一边听音乐一边看网页,播放音乐是一个任务,显示网页又是一个任务,若任务无法同时进行的话,难道要先把音乐放完才能看网页吗?
虽然现在CPU是多核心,但打开工作管理员会发现任务实在太多了,所以通常一核心的CPU要同时处理多件事情,那就只能让任务各种穿插执行了,让人脑感觉好像是同时进行一样,像下面这张图:
来源网址:https://laike9m.com/blog/huan-zai-yi-huo-bing-fa-he-bing-xing,61/
从上一篇文章python的asyncio模组(一):异步执行的好处就能知道io时间比CPU执行时间要花费更久,所以为了不要让CPU去等待io而浪费时间,我们就可以趁这段时间去切换执行其他任务,比如说播放音乐,从本地端或是网路上读取音乐档就是需要调动io,这时就可以利用中间小段空闲时间去开启并显示网页,等到io调动时间结束,就可以重新执行原本任务了。
一、事件迴圈(Event loop)
既然异步程式可以在多个任务之间切换,那想必这个程式的架构里一定有一个任务的list,这样程式才知道有什么样的任务可以做切换,而这个任务的list以及中间切换的机制,就是由事件迴圈(Event loop)来处理。
二、事件(Event)与回调函数(Callback)
现在Event loop裏面有一个list,若程式有一些任务需要以异步的方式去执行,那就需要以"Event:Callback"的型式注册进我们Event loop的list裏面,之后Event loop以for迴圈的方式去察看list裏面的Event是否发生,若发生了就执行相对应的Callback,并注销这个Event的监听。
打个比方,我今天对Event loop注册了三个任务:
"Event_A:Callback_A""Event_B:Callback_B""Event_C:Callback_C"然后Event loop会如以下的方式循环的对list里面的事件做监听:
假设正在监听的Event_B发生了,就会注销Event_B的监听(也就是把Event_B从list中移除),然后执行Callback_B,执行完后在继续监听剩下的事件。
但上图的解释可能会出现一个疑问,里面的意思是执行完Callback_B之后才会继续监听,但假设在执行Callback_B这个任务的中途,出现了需要调动io的指令,不就应该要先放下Callback_B这个任务,去监听其他事件并执行其他任务吗?为什么要等到Callback_B执行完呢?
没错,异步程式设计应该要在遇到io读取的时候切换其他任务去执行,但上图确实是Event_loop的实作机制,所以如果你的Callback_B的内容像以下这样:
def Callback_B(): do_some_work1() read_from_io_and_wait() # 读取io的指令,等到读取完成才能执行下一个指令 do_some_work2() return
你的程式是没有办法中途去切换其他任务的,若要能够切换其他任务,应该要设计成以下型式:
def Callback_B(): do_some_work1() read_from_io_and_not_wait() # 读取io的指令,不用等到读取完成就直接执行下一个指令 register_to_EventLoop("finish read from io",Callback_D) returndef Callback_D(): # 等到Callback_B读取io的指令完成并被EventLoop监听到就执行 do_some_work2() return
事实上如果Callback_B若要有遇到调动io指令就暂停执行的功能,那他应该要设计成遇到调动io指令,就多注册一个"Event_D:Callback_D"进去Event loop list,这个Event_D指的是"调动io完成",Callback_D的任务範围是完成调动io"后"所要执行的指令,而Callback_B的任务範围应该只有完成调动io"前"所要执行的指令。
下一篇教学:
python的asyncio模组(三):建立Event Loop和定义协程
参考资料:
Event loop的实作细节
http://lotabout.me/2017/understand-python-asyncio/