蓝多多在做QQ群机器人插件的时候遇到了async与await的使用,自己在百度上查了些资料,现在记录汇总一下。
目录
一、基本概念(源:协程和异步io - biu嘟 - 博客园):
二、进程、线程、协程的特点(源:异步、并发、协程原理 - 成都发哥 - 博客园):
一、基本概念(源:协程和异步io - biu嘟 - 博客园):
并发:指两个或多个事件在同一时间间隔内发生。是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机上运行。
并行:指两个或者多个事件在同一时刻发生,即是指任何时间点,有多个程序运行在多个CPU上。
同步:是指代码调用IO操作时,必须等待IO操作完成才能返回的调用方式。
异步:是指代码调用IO操作时,不必等待IO操作完成就能返回的调用方式。
协程:协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。传统函数调用的过程为 A->B->C->D;我们需要一个可以暂停的函数,并且可以在适当的时候恢复该函数的继续执行,这便是协程,当程序中方法需要等待时间的话,就可以用协程。
二、进程、线程、协程的特点(源:异步、并发、协程原理 - 成都发哥 - 博客园):
进程(process):
1、进程是资源分配的最小单位。
2、进程间不共享内存,每个进程拥有自己独立的内存。
3、进程间可以通过信号、信号量、共享内存、管道、队列等来通信。
4、进程由操作系统调度。
线程(thread):
1、线程是程序执行流的最小单位。
2、线程是来自于进程的,一个进程下面可以开多个线程。
3、每个线程都有自己一个栈,不共享栈,但多个线程能共享同一个属于进程的堆。
4、线程因为是在同一个进程内的,可以共享内存。
5、线程也是由操作系统调度,线程是 CPU 调度的最小单位。
6、新开线程开销小于进程,CPU 在切换线程成本也小于进程。
7、某个线程发生致命错误会导致整个进程崩溃。
协程(coroutine):
1、对于操作系统来说只有进程和线程,协程的控制由应用程序显式调度,非抢占式。
2、协程的执行最终靠的还是线程,应用程序来调度协程选择合适的线程来获取执行权。
3、切换非常快,成本低,创建和切换的消耗更低。
三、async/await 使用
函数在正常执行的过程中是不会发生中断的,所以如果需要写一个能够中断的函数,则需要添加async关键字。
async 用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(例:挂起条件是sleep(1))消失后,也就是1秒到了再回来执行被挂起的函数。
await 用来用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂起,去执行其他的异步程序。await 后面只能跟异步程序或有__await__属性的对象。
四、程序实例
1、机器人撤回含敏感词消息的函数
async def c(bot: Bot, event: GroupMessageEvent, state: T_State): mid = event.message_id print(mid) await bot.delete_msg(message_id=mid)
其中函数c采用async来声明为一个异步函数,函数c为异步函数;await声明了程序的挂起,后面跟随bot.delete_msg()这一异步函数,当执行到await bot.delete_msg(message_id=mid)这句代码时异步函数(程序)c挂起,去执行异步函数(程序)bot.delete_msg(),当挂起条件失效后,从异步函数bot.delete_msg()中跳出,继续执行原来函数(程序)c的操作。
通俗的来讲,设两个异步函数async def a(),async def b(),a函数中存在某一步具有有await关键字,当程序碰到关键字await b()后,异步程序a挂起,转去执行异步b程序,当挂起条件消失后,不论程序b是否执行完,都要从程序b中跳出,继续执行原程序原来的操作。 如果await后面跟的b函数不是异步函数,挂起条件不会消失,那么程序只能等函数b执行完再返回,无法在b执行的过程中返回。即与直接调用函数b相同,无需await关键字。在一个异步函数中,可以不止一次挂起,也就是可以用多个await。
2、博客园给出的代码实例:(源:对python async与await的理解 - xinghun85 - 博客园)
import asyncio import time import requests async def test2(i): r = await other_test(i) print(i, r) async def other_test(i): r = requests.get(i) print(i) await asyncio.sleep(4) print(time.time() - start) return r url = ["https://segmentfault.com/p/1210000013564725", "https://www.jianshu.com/p/83badc8028bd", "https://www.baidu.com/"] loop = asyncio.get_event_loop() task = [asyncio.ensure_future(test2(i)) for i in url] start = time.time() loop.run_until_complete(asyncio.wait(task)) endtime = time.time() - start print(endtime) loop.close()
程序运行结果:
可以自己根据程序的输出顺序来判断执行顺序,这里就不赘述了,感觉很清晰。
3、机器人发送语音函数分析(源:【qq机器人】机器人发语音_python菜鸟-CSDN博客)
test = on_command('语音',rule=to_me(),priority=5) @test.handle() async def h_r(bot: Bot, event: Event, state: T_State): ms = await ma() chuo = f"[CQ:tts,text={ms}]" await test.send(Message(chuo)) async def ma(): url = 'https://api.sunweihu.com/api/yan/api.php' resp = requests.get(url) return resp.text
当机器人收到@ +关键字“语音”时,会执行异步函数h_r(),执行到ms = await ma()语句时遇到await关键字,挂起程序h_r(),跳出并转入执行异步函数ma()进行声音的爬取操作,当挂起条件消失时(这里是爬取到音频后),完成函数ma()的执行,返回继续执行函数h_r(),当执行到await test.send(Message(chuo))语句时,又遇到await关键字,再次挂起程序h_r()转而执行消息发送程序test.send()致消息发送完毕,返回执行函数h_r(),因为 await test.send(Message(*))是函数h_r()的最后一句,所以到此完成了机器人获取发送语音指令到发送语音的过程。
个人理解,如有错误,欢迎评论区指正。谢谢