python之协程开发

greenlet详解

什么是 greenlet ?

虽然CPython(标准Python)能够通过生成器来实现协程,但使用起来还并不是很方便。
与此同时,Python的一个衍生版 Stackless Python实现了原生的协程,它更利于使用。
于是,大家开始将 Stackless 中关于协程的代码单独拿出来做成了CPython的扩展包。
这就是 greenlet 的由来,因此 greenlet 是底层实现了原生协程的 C扩展库

yield的使用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/10/17 11:47
# @Author  : DoubleChina
# @Site    : 
# @File    : GeventTest.py
# @Software: PyCharm

# yield
# 迭代器 iterable,itertor ,iterion
#
def fib(n):
    index = 0
    a = 0
    b = 1
    while index < n:
        # 会暂停,阻塞,会返回值
        # c是接受外面的数据, b是把值传输给外面
        c = yield b
        print('task...{}'.format(c))
        a, b = b, a + b
        index += 1


def yield_test():
    f = fib(10)
    # 从循环中返回一个值
    # print(f.__next__())
    n = next(f)
    while True:
        print(n)
        try:
            n = f.send(3)
        except StopIteration:
            break


if __name__ == '__main__':
    yield_test()

# 协程就理解为可以切换的函数或者生成器

yield实现生产者和消费者


def Cunsumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('Cunsumer{}'.format(n))
        r = '200'


def Produce(c):
    c.send(None)  # 启动生成器,把代码运行到yield这样
    n = 0
    while n < 5:
        n = n + 1
        r = c.send(n)
        print('Produce{}'.format(r))
    c.close()


if __name__ == '__main__':
    c = Cunsumer()
    Produce(c)

安装greenlet

#安装: greenlet
pip install greenlet
#安装:gevent
pip install gevent

使用greenlet实现生产者和消费者

greenlet优势:

  • 高性能的原生协程
  • 语义更加明确的显式切换
  • 直接将函数包装成协程,保持原有代码风格

from greenlet import greenlet
import random
import time


def Producer():
    while True:
        item = random.randint(0, 10)
        print('生成了{}'.format(item))
        c.switch(item)  # 切到c
        time.sleep(1)


def Consumer():
    while True:
        item = p.switch()  # 切到P
        print('消费了{}'.format(item))


if __name__ == '__main__':
    c = greenlet(Consumer)
    p = greenlet(Producer)
    c.switch()

gevent详解

gevent通过greenlet实现协程,核心就是在遇到IO操作,会自动切换到其他的协程上
虽然,我们有了 基于 epoll 的回调式编程模式,但是却难以使用。
即使我们可以通过配合 生成器协程 进行复杂的封装,以简化编程难度。
但是仍然有一个大的问题: 封装难度大,现有代码几乎完全要重写
gevent,通过封装了 libev(基于epoll) 和 greenlet 两个库。
帮我们做好封装,允许我们以类似于线程的方式使用协程。
以至于我们几乎不用重写原来的代码就能充分利用 epoll 和 协程 威力。

优势:

  • 使用基于 epoll 的 libev 来避开阻塞
  • 使用基于 gevent 的 高效协程 来切换执行
  • 只在遇到阻塞的时候切换,没有轮需的开销,也没有线程的开销

简单使用gevent

#记得把他写在第一行
from gevent import monkey

# 会把python标准库中的一些阻塞操作变为非阻塞
monkey.patch_all()
import gevent
import time


def test1():
    print(12)
    gevent.sleep(2)
    print(34)


def test2():
    print(56)
    gevent.sleep(1)
    print(78)


if __name__ == '__main__':
    # spawn 启动协程,参数就是函数的名字
    # joinall 阻塞当前流程,执行给定的 greenlet
    gevent.joinall([gevent.spawn(test1), gevent.spawn(test2)])
    print(1111)

使用gevent实现队列

from gevent import monkey

monkey.patch_all()

from gevent.queue import Queue
import gevent
import random

queue = Queue(3)


def Producer(queue):
    while True:
        item = random.randint(0, 10)
        print('生成了{}'.format(item))
        queue.put(item)
        gevent.sleep(1)


def Consumer(queue):
    while True:
        item = queue.get()
        print('消费了{}'.format(item))


if __name__ == '__main__':
    # spawn 启动协程,参数就是函数的名字
    # joinall 阻塞当前流程,执行给定的 greenlet
    g1 = gevent.spawn(Producer, queue)
    g2 = gevent.spawn(Consumer, queue)
    # 阻塞(一阻塞就切换协程)
    gevent.joinall([g1, g2])

你可能感兴趣的:(python爬虫,PyCharm,python,Python进阶之路,协程,并发,python,greenlet,gevent)