协程基础
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结束