12. 协程的使用

发布时间 2023-10-19 17:40:56作者: 星河映梦

一、什么是协程

  协程(Coroutine),又称 微线程。协程可以在单线程的情况下实现并发。程序员在代码层面上检测所有的 IO 操作,一旦遇到 IO 操作,程序员在代码级别完成切换,这样给 CPU 的感觉就是这个程序一直运行,没有 IO 操作,从而提升程序的运行效率。

  协程可以理解为,在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另一个函数中执行。注意,不是通过调用函数的方式做到的, 并且切换的次数以及什么时候切换到原来的函数都是开发者自己确定。

import time

def task1():
    while True:
        print("task1 work!")
        yield
        time.sleep(0.5)

def task2():
    while True:
        print("task2 work!")
        yield
        time.sleep(0.5)

if __name__ == "__main__":
    t1 = task1()
    t2 = task2()

    while True:
        next(t1)
        next(t2)

二、使用greenlet模块实现协程

  进程、线程创建完之后,到底是哪个进程、线程执行去执行,这是不确定的,这需要有操作系统来进行计算(调度算法,例如优先级调度)。而协程是可以人为来控制的。我们可以使用 greenlet 模块实现协程,但还需要人工切换。

  我们可以在终端中使用 pip 命令安装 greelet 模块:

pip install greenlet
import time

from greenlet import greenlet

def task1():
    while True:
        print("task1 work!")
        g2.switch()
        time.sleep(0.5)

def task2():
    while True:
        print("task2 work!")
        g1.switch()
        time.sleep(0.5)

if __name__ == "__main__":
    g1 = greenlet(task1)
    g2 = greenlet(task2)

    g1.switch()

三、使用gevent模块实现协程

  greenlet 模块已经实现了协程,但还需要人工切换。Python 还提供了一个能自动切换任务的 gevent 模块。其原理是当一个 greenlet 遇到 IO(指的是 input output,输入输出,比如,网络、文件操作等)救护自动切换到其它的 greenlet 再执行,而不是等待 IO。

  我们可以在终端中使用 pip 命令安装 gevent 模块:

pip install gevent
import time

from gevent import spawn

# gevent模块本身无法检测常见的一些IO操作
# 在使用的时候需要额外导入monkey模块
from gevent import monkey
monkey.patch_all()

def say_hello():
    print("hello")
    time.sleep(2)
    print("hello")

def say_hi():
    print("hi")
    time.sleep(3)
    print("hi")

def say_good():
    print("good")
    time.sleep(5)
    print("good")

start_time = time.time()

g1 = spawn(say_hello)
g2 = spawn(say_hi)
g3 = spawn(say_good)

# 等待被检测的任务执行完毕,再往后执行
g1.join()
g2.join()
g3.join()

print(time.time() - start_time)
import gevent
import time

# gevent模块本身无法检测常见的一些IO操作
# 在使用的时候需要额外导入monkey模块
from gevent import monkey
monkey.patch_all()

def say_hello():
    print("hello")
    time.sleep(2)
    print("hello")

def say_hi():
    print("hi")
    time.sleep(3)
    print("hi")

def say_good():
    print("good")
    time.sleep(5)
    print("good")

start_time = time.time()

gevent.joinall([
    gevent.spawn(say_hello),
    gevent.spawn(say_hi),
    gevent.spawn(say_good),
])

print(time.time() - start_time)

time 模块的 sleep() 方法不具备自动切换任务的功能,而 gevent 模块的 sleep() 方法具有该能功能,所以我们使用猴子补丁将 time 模块的 sleep() 方法编程 gevent 模块的 sleep() 方法;