协程,greenlet原生协程库, gevent库

Posted pywjh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协程,greenlet原生协程库, gevent库相关的知识,希望对你有一定的参考价值。

yield表达式

   在了解协程之前,需要先了解一下生成器中的yield,它不仅可以当做生成器,还能当做一个表达式来使用(yield)

def func():
    x = (yield)
    print(x)
    x = (yield)
    

g = func()
print(next(g))  # 这是第一个yield,就暂停了
g.send(hello world)   # 恢复暂停位置,将第一个yield赋值,
                        # x = hello world,然后又执行到yield,暂停


-->
None
hello world

Process finished with exit code 0

 

需要注意的是:    send跟next一样,可以继续暂停的执行,并把send括号里面的东西变成返回值

        没有next开始,就不能使用send!

协程下的生产者与消费者问题

import random, time


def consumer():
    while True:
        item = (yield)  # 接收send的传递内容
        print(消费了%s % item)
        time.sleep(1)


def producer(consumer): # 主函数
    next(consumer)  # 开启协程
    while True:
        item = random.randint(0, 99)
        print(生产了%s % item)
        consumer.send(item)     # 发送
        time.sleep(1)


c = consumer()  # 生成器对象
producer(c)   

greenlet原生协程

  greenlet属于三方库,通过 pip install greenlet 安装

  什么是greenlet呢?

    CPython(标准python)能够通过生成器来实现协程,但使用起来并不方便而python的衍生版Stackless python,实现了原生的协程,便于利用。

    于是将stackless中关于协程的代码单独拿出来做成了CPython的扩展包

    也就是python环境下的原生协程包

  greenlet的价值

    一  高性能的远程协程

    二   语义更加明确的显示切换

    三   直接将函数包装成协程,保持原有代码风格

greenlet下的生产者与消费者问题

from greenlet import greenlet
from time import sleep
from random import randint

def consumer():
    while True:
        item = p.switch()   # 切换p,开始暂停,等待恢复(只有恢复的时候才能收到数据)
        print(消费了%s % item)
        sleep(1)


def producer():
    while True:
        item = randint(0, 99)
        print(生产了%s % item)
        c.switch(item)      # 暂停当前协程,并且切换到指定的协程,传参
        sleep(1)

c = greenlet(consumer)  # 直接封装成协程
p = greenlet(producer)
c.switch()  # 相当于next的作用,开启协程

 gevent协程

  什么是gevent?

    gevent开始之前可以先了解一下IO多路复用的epoll

    gevent通过封装了libev(基于epoll)和greenlet两个库,可以实现类似线程方式的协程

    gevent = epoll + greenlet

  gevent的价值是什么?

    遇到阻塞就切换到另一个协程继续执行

      一  使用基于epoll的libev来避开阻塞

      二   使用基于gevent的高效协程来切换执行

      三   只有阻塞的时候切换,没有轮询的开销,也没有线程的开销

gevent实现并发服务器

from gevent import monkey; monkey.patch_socket()
# 猴子补丁,将socket替换成一个封装了epoll的socket
from socket import socket
import gevent


server = socket()   # 封装好的socket
server.bind((‘‘, 7788))
server.listen(1000)

def recv(conn):
    while True:
        recv_date = conn.recv(1024).decode()
        if recv_date:
            print(recv_date)
            conn.send(recv_date.encode())
        else:
            conn.close()
        
while True:
    conn, addr = server.accept()
    # 生成一个协程,并将conn作为参数传入
    gevent.spawn(recv, conn)

 

 

 

 

gevent实现生产者与消费者问题

import gevent
from gevent import monkey; monkey.patch_all()
# all是所有的能切换成协程的地方全部切换,他包含了socket,一般情况下都是用all
from random import randint
from time import sleep
from gevent.queue import Queue  # gevent里面的队列

queue = Queue(3)

def consumer():
    while True:
        item = queue.get()  # 空就阻塞
        print(消费了%s % item)
        sleep(1)

def producer():
    while True:
        item = randint(0, 99)
        queue.put(item)     # 满就阻塞 epoll阻塞就切换
        print(生产了%s % item)
        sleep(1)

c = gevent.spawn(consumer)  #封装成协程
p = gevent.spawn(producer)
gevent.joinall([c, p])  # 等待传入协程结束

 



以上是关于协程,greenlet原生协程库, gevent库的主要内容,如果未能解决你的问题,请参考以下文章

006py协程gevent

python的gevent协程

并发编程之协程

用gunicorn+gevent启动Flask项目

day⑨: 协程_gevent

网络编程之协程——gevent模块