12. 协程
计算机中提供了:线程、进程用于实现并发编程。
协程(Coroutine):是程序员通过代码实现出来的(非真实存在),用户态、可暂停/可恢复的函数。 它能在运行中把控制权"主动让出",之后再从让出的地方继续执行。和线程相比,它不靠操作系统抢占调度,而是**主动配合(cooperative)**切换,因此轻量、切换快、能在单线程里跑出"并发感"。
协程也可以被称为微线程,是一种用户态内的上下文切换技术。 简而言之,其实就是通过一个线程实现代码块相互切换执行(来回跳着执行)
例如:
python
def func1():
print("func1-1")
print("func1-2")
def func2():
print("func2-1")
print("func2-2")
func1()
func2()上述代码是普通的函数定义和执行,按照流程分别执行两个函数中的代码,并先后会输出:func1-1、func1-2、func2-1、func2-2。
但如果介入协程技术那么就可以实现函数间代码切换执行,最终输出:func1-1、func2-1、func1-2、func2-2。
实现协程
greenlet
python
from greenlet import greenlet
def func1():
print(1)
gr2.switch() # 切换到 func2 函数
print(2)
gr2.switch()
def func2():
print("A")
gr1.switch() # 切换回 func1 函数,从上一次执行的位置继续向后执行
print("B")
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 去执行 func1 函数
# 输出结果:
# 1
# A
# 2
# Byield
python
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield "A"
yield "B"
f1 = func1()
for item in f1:
print(item)
# 输出结果:
# 1
# A
# B
# 2虽然上述两种都实现了协程,但这种编写方式没有意义,手动来回切换执行,可能让程序执行速度更慢
asyncio
python
import asyncio
async def func1():
print(1)
await asyncio.sleep(1)
print(2)
async def func2():
print(3)
await asyncio.sleep(1)
print(4)
tasks = [asyncio.ensure_future(func1()), asyncio.ensure_future(func2())]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# 输出结果:
# 1
# 3
# 2
# 4协程、线程、进程的区别
线程,是计算机中可以被 CPU 调度的最小单元。
进程,是计算机资源分配的最小单元(进程为线程提供资源)。
一个进程中可以有多个线程,同一个进程中的线程可以共享此进程中的资源。
进程与进程之间是相互独立的,进程之间的数据不共享。
由于 CPython 中 GIL 的存在:
- 线程,适用于 IO 密集型操作。
- 进程,适用于计算密集型操作。
协程,协程也可以被称为微线程,是一种用户态内的上下文切换技术。在开发中结合遇到 IO 自动切换,就可以通过一个线程实现并发操作。
所以,在处理 IO 操作时,协程比线程更加节省开销(协程的开发难度大一些)。