协程(coroutine)可以理解为是线程的优化,又称之为轻量级进程。它是一种比线程更节省资源、效率更高的系统调度机制。
协程具有这样的特点,即在同时开启的多个任务中,一次只执行一个,只有当前任务遭遇阻塞,才会切换到下一个任务继续执行。这种机制可以实现多任务的同步,又能够成功地避免线程中使用锁的复杂性,简化了开发。
早先的协程是使用生成器关键字 yield 来实现的,代码特别复杂难懂。自从 Python 3.5之后,确定了协程的语法,使得创建协程的方式得到改善。Python 中,能够实现协程的模块有多个,如 asyncio、tornado 或 gevent。
这里以 asyncio 为例,先来了解一下创建协程所用到的概念:
- event_loop(事件循环):是一个协程处理函数的调用机制。程序会开启一个无限循环,当事件发生时,调用相应的协程函数。
- coroutine(协程对象):指一个使用 async 关键字来定义的函数。调用该函数,会返回一个协程对象。该协程对象就是一个处于挂起状态的协程函数,需要注册到事件循环 event_loop 中,由事件循环 event_loop 进行调用。
- task 任务:是对协程的进一步封装。
- future:等同于 task,代表执行任务的结果。
- async/await 关键字:Python 3.5 中用于定义协程的关键字,其中 async 用于定义一个协程,await 用于挂起阻塞的异步调用接口。
Python协程的基本用法
下面通过代码演示最基本的协程用法:
# 导入asyncio模块 import asyncio #定义协程处理函数 async def demo(x): print(x) r = await asyncio.sleep(1) print(x,"again") #生成协程对象,并传入 hello coroutine = demo("C语言中文网") loop = asyncio.get_event_loop() try: #将协程注册到实现事件循环对象中,并开始运行。 loop.run_until_complete(coroutine) finally: #程序结束关闭事件循环对象 loop.close()
运行结果为:
C语言中文网
C语言中文网 again
注意,以上程序代码只能用在 Python 3.5 以及后续版本。因为 async 和 await 关键字是 3.5 版本才新加入。并且,在 Python 3.7 版本及后续版本中,可以使用 asyncio.run(coroutine) 代替第10~16行的代码,它们是完全等价的。
上面的代码体现了实现协程的基本步骤:
- 使用 async 关键字定义协程处理函数;
- 生成协程对象;
- 调用 asyncio 中的 get_event_loop 函数获得事件循环对象;
- 调用事件循环对象的 run_until_complete 方法,运行协程处理函数。
协程VS线程
与线程相比,协程封装的内容更多,实现起来更容易。
开发者使用协程会少考虑很多事情。在默认情况下应优先使用协程。但是有一个特点要注意,协程是多任务的顺序执行,只有当前任务挂起后,才会切换到其他任务来执行;而线程是以 CPU 轮换的方式执行。
因此,两者的运行机制有着本质上的区别,这使得二者都不可被代替。也就是说,如果业务需求要每个并行线程的处理进度也要同步,那么使用协程将不是一个很好的方案。在实际工作中,应根据业务需要来灵活运用线程或协程。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/22950.html