python学习笔记 day40 协程

1. 协程

进程是CPU资源分配的最小单位; 线程是CPU调度的最小单位;

协程就是在一个线程内切换执行任务;

之前使用yield也可以实现在一个主线程中切换执行:

def func1():  # 生成器函数
    print("吃饭吗")
    yield
    print("那我们走吧")
    yield

def func2():
    g=func1()  # 生成器函数在被调用时不会立即执行,除非next(g)触发才可以
    next(g)    # 开始执行func1()函数,但是遇到yield就会停止
    print("")
    next(g)
    print("好的,走吧")

func2()

使用yield实现生产者消费者模型;

import random
import time
def consumer():  # 消费者模型
    while True:
        n=yield   # yield接收g.send()的结果,然后赋值给n
        time.sleep(random.random())
        print("消费了%s"%n)

def procuder():  # 生产者模型
    g=consumer()  # 调用生成器函数,并不会理解执行生成器函数内部的代码(除非next()进行触发)
    next(g)  # 开始执行生成器函数
    for i in range(10):
        print("生产了%s"%i)
        g.send(i)  # send()给生成器函数yield处接收
procuder()

运行结果::

 2. greenlet 实现同一线程内切换

from greenlet import greenlet
def func1():
    print("hello,xuanxuan")
    g2.switch()   # 切换执行func2()
    print("byte~ xuanxuan")
    g2.switch()
def func2():
    print("hello,xixi")
    g1.switch()  # 切换执行func1()
    print("bye~")
g1=greenlet(func1)
g2=greenlet(func2)
g1.switch()  # 切换执行func1

运行结果:

 另外需要注意greenlet切换时是不规避IO时间的:

from greenlet import greenlet
import time
def func1():
    print("func1:吃饭去吗")
    g2.switch()
    time.sleep(2)   # greenlet进行切换时,并不会规避掉IO时间(也就是切换回来时还是需要等待2秒在执行)
    print("func1:那我们走吧")
    g2.switch()

def func2():
    time.sleep(2)
    print("func2:吃")
    g1.switch()
    time.sleep(3)
    print("func2:好的")
g1=greenlet(func1)
g2=greenlet(func2)
g1.switch()

运行结果: 

 

如果在同一个程序有IO的情况下,才切换会让效率提高很多,但是yield greenlet均不会在切换时规避掉IO时间

其实就相当于原来func1需要等待3秒,但是如果不切换,顺序执行,那么func2也会跟着等待3秒,浪费了两个人的时间,但是如果此时func1这时切换到让func2执行,就可以不让fun2等待func1需要等待的时间,就可以某种程度上实现并发(有点牵强,但是确实有一种func1 func2交替执行,你一句我一句这种并发的效果)

 3. gevent---实现协程切换

import gevent

def func1():
    print("吃饭去吗")
    gevent.sleep(2)  # gevent可以在gevevt.sleep()自己认识的IO操作切换
    print("那我们走吧")
    gevent.sleep(2)
def func2():
    print("")
    gevent.sleep(2)
    print("好的")
g1=gevent.spawn(func1)
g2=gevent.spawn(func2)
# g1.join()  # 主线程等待结束
# g2.join()
gevent.joinall([g1,g2])  # 相当于上面的g1.join()  g2.join()

gevent 就是当遇到gevent.sleep() IO 时会自动切换;

gevent()对普通的IO (比如time模块的sleep,socket 以及urllib request等网络请求)是无法切换的:

import gevent
import time
def func1():
    print("func1:吃饭去吗")
    time.sleep(2)  # gevent对普通模块的IO比如time.sleep()是无法完成切换的
    print("func1:那我们走吧")
    time.sleep(1)
def func2():
    print("func2:吃")
    time.sleep(2)
    print("func2:好的")
g1=gevent.spawn(func1)
g2=gevent.spawn(func2)
gevent.joinall([g1,g2])  # 等待所有函数执行完 因为主线程跟使用gevent 切换函数执行是完全异步的

运行结果:

那如果想在使用gevent在单线程执行函数内遇到其他模块的IO(time.sleep()  socket模块的accept recv 以及urllib requests的网络请求)之间切换 可以在所有IO请求的模块上面加上一句: from gevent import monkey;monkey.patch_all() 即可实现gevent在单线程 执行函数内遇到其他模块的IO 也可以完成切换:

from gevent import monkey;monkey.patch_all()  # 就可以使用gevent在单线程执行函数内遇到其他模块的IO操作时也可以完成切换
# 但是这句话必须加在其他模块的最上面(socket time等包含IO的模块)
import time
import gevent
def func1():
    print("func1:吃饭去吗")
    time.sleep(2)  
    print("func1:那我们走吧")
    time.sleep(1)
def func2():
    print("func2:吃")
    time.sleep(2)
    print("func2:好的")
g1=gevent.spawn(func1)
g2=gevent.spawn(func2)
gevent.joinall([g1,g2])  # 等待所有函数执行完 因为主线程跟使用gevent 切换函数执行是完全异步的

运行结果:

talk is cheap,show me the code
原文地址:https://www.cnblogs.com/xuanxuanlove/p/9798451.html