协程基础

2020-05-09  本文已影响0人  小吉头

一、概念

协程是通过一个线程在不同代码块之间切换执行,从而实现多任务(这里是并发,假的多任务)
并发:一个时间段内,几个程序在同一cpu上运行,但是一个时刻点只有一个程序在cpu上运行
并行:任意时刻点上,多个程序运行在多个cpu上

二、实现方式

(1) yield实现

import time

def task1():
    while True:
        print("---1---")
        time.sleep(1)
        yield

def task2():
    while True:
        print("---2---")
        time.sleep(1)
        yield


def task3():
    while True:
        print("---3---")
        time.sleep(1)
        yield

def main():
    t1 = task1()
    t2 = task2()
    t3 = task3()
    while True:
        next(t1)
        next(t2)
        next(t3)

if __name__ == "__main__":
    main()

遇到yield就返回,开始执行下一个生成器。本质是一个线程在三个生成器之间不断切换执行。

(2) greenlet实现

yield方式每次都要返回main函数,然后再去进入到另一个代码块,再返回main函数,再进入下一个代码块... greenlet是基于yield的封装, 只要启动greenlet对象后,代码块之间可以自由切换,比yield方便。

from greenlet import greenlet # 如果没有该模块,通过pip install greenlet
import time

def task1():
    while True:
        print("---1---")
        g2.switch()
        time.sleep(1)

def task2():
    while True:
        print("---2---")
        g1.switch()
        time.sleep(1)

g1 = greenlet(task1)
g2 = greenlet(task2)

g1.switch()

(3) 使用gevent

greenlet还是要人为在代码块中设置切换点,gevent是对greenlet的封装,当线程遇到耗时操作能自动切换到其他代码块,再在适当的时候切换回来继续执行。yield和greenlet不是真正意义的多任务,因为遇到耗时操作(比如代码中的sleep())线程会卡在那边等待延时结束。

import gevent
from gevent import monkey
import time

monkey.patch_all() # 自动将协程代码块中的耗时函数替换成gevent对象中的耗时函数

def g1(n):
    for i in range(n):
        print("g1--->{0}".format(i))
        time.sleep(3)

def g2(n):
    for i in range(n):
        print("g2--->{0}".format(i))
        time.sleep(2)

def g3(n):
    for i in range(n):
        print("g3--->{0}".format(i))
        time.sleep(1)

gevent1 = gevent.spawn(g1,3)#创建协程对象,是对greenlet的封装
gevent2 = gevent.spawn(g2,3)
gevent3 = gevent.spawn(g3,3)

gevent3.join()# join()表示阻塞,等待gevent3对象执行完成再返回。这时线程发现gevent3.join()是耗时操作,于是从最先创建的协程gevent1开始执行
              # 根据创建顺序,gevent1、gevent2、gevent3会轮流执行,遇到耗时操作sleep()时,线程会自动切换,一直到gevent3对应的代码块执行完返回
print("gevent3结束")
gevent2.join()
print("gevent2结束")
gevent1.join()#此时gevent1代码块已全部执行完毕,无需阻塞
print("gevent1结束")

#结果如下
g1--->0
g2--->0
g3--->0
g3--->1
g2--->1
g3--->2
g1--->1
gevent3结束
g2--->2
g1--->2
gevent2结束
gevent1结束
上一篇下一篇

猜你喜欢

热点阅读