协程框架---gevent的基本介绍

1.基本介绍

一个基于greenlet的并发网络库。有了gevent后,不必像greenlet那样手动切换,而是当一个协程阻塞时,将自动切换到其他协程。

import gevent

def test1():
    print('test1.start')
    gevent.sleep(0)
    print('test1.end')

def test2():
    print('test2.start')
    gevent.sleep(0)
    print('test2.end')

gevent.joinall([
        gevent.spawn(test1),
        gevent.spawn(test2)
    ])

gevent.spawn()会创建一个协程并且运行

gevent.sleep()会造成阻塞,切换到其他协程继续执行

gevent.joinall([])会等待所有传入的协程运行结束后再退出

(joinall()还可以接受一个timeout参数,设置超时时间)

与greenlet不同的是,当一个协程运行完成后,会自动切换到其他未完成的协程中运行,所以上段程序打印为

打印结果:

test1.start

test2.start

test1.end

test2.end

import gevent
import socket

urls = ['www.baidu.com','www.gevent.org','www.python.org','www.google.com','www.123.com']
jobs = [gevent.spawn(socket.gethostbyname,url) for url in urls]
gevent.joinall(jobs,timeout=5)

for job in jobs:
    print(job.value)

通过协程来分别打开三个网址的IP地址,由于打开远程地址时造成IO阻塞,所以gevent会自动调度不同的协程

2.猴子补丁

python的socket库是阻塞式的,DNS无法并发解析,所以上一个例子中使用协程是起不到节省时间的目的的。

可以使用猴子补丁(Monkey Patching)来解决这个问题。

只需要在上一个例子最上面加入两行代码

from gevent import monkey
monkey.patch_all()

对socket标准库打上猴子补丁,此后socket标准库中的类和方法都变成了非阻塞式,其他的所有代码都不需要更改

这样协程的效率就能体现出来了

monkey.patch_all()将把python中所有的标准库都替换成非阻塞式

3.获取协程状态

started属性判断协程是否已启动

ready()函数判断是否已停止

successful()函数判断是否成功运行并且没有抛出异常

value属性,如果运行成功并且有返回值,那么可以通过value获取到

exception,在greenlet协程运行过程中抛出异常是不会抛出到协程外的,因此需要主动捕获异常

import gevent

def win():
    return 'You Win'

def fail():
    raise Exception('You Fail')


winner = gevent.spawn(win)
loser = gevent.spawn(fail)

print('winner.started =',winner.started)
print('loser.started =',loser.started)

# 在Greenlet中发生的异常,不会被抛到Greenlet的外面
# 控制台会打印错误信息,但程序不会中断
try:
    gevent.joinall([
            winner,
            loser
        ])
except Exception as e:
    # 这段不会执行
    print(e.args)

print('winner.ready() =',winner.ready())
print('loser.ready() =',loser.ready())

print('winner.value =',winner.value)
print('loser.value =',loser.value)

print('winner.successful() =',winner.successful())
print('loser.successful() =',loser.successful())

# 获取异常信息
print('loser.exception =',loser.exception)
# get函数会将loser的异常抛出,如果不捕获,程序会中断
try:
    print(loser.get())
except:
    print('exception from loser')

打印LOG:

winner.started = True

loser.started = True

Traceback (most recent call last):

File "E:\Documents\Anaconda\lib\site-packages\gevent\greenlet.py", line 536, in run

result = self._run(*self.args, **self.kwargs)

File "E:\Code\Python\Test\test6.py", line 7, in fail

raise Exception('You Fail')

Exception: You Fail

Sun Jul 8 17:31:11 2018 <Greenlet at 0x142a32a79c8: fail> failed with Exception



winner.ready() = True

loser.ready() = True

winner.value = You Win

loser.value = None

winner.successful() = True

loser.successful() = False

loser.exception = You Fail

exception from loser


4.协程运行超时

前面将可以在gevent.joinall()函数传入timeout参数来设置超时时间,也可以设置全局超时时间

import gevent
from gevent import Timeout

timeout = Timeout(2)
timeout.start()

def wait():
    gevent.sleep(10)

try:
    gevent.spawn(wait).join()
except Timeout:
    print('Could not complete')

在这个例子中,所有的协程的超时时间都设置成2秒,如果2秒内协程没有结束,都将抛出Timeout异常

也可以将超时设置在with语句内,那么设置就只在with内部生效了

with Timeout(2):
    gevent.sleep(10)

也可以设置当超时时抛出指定的异常

with Timeout(2,Exception):
    gevent.sleep(10)

5.协程间通讯

wait()可以阻塞协程

set()可以唤醒所有阻塞的异常

import gevent
from gevent.event import Event

evt = Event()

def setter():
    print('Wait for me')
    gevent.sleep(3)
    print('After 3 sec, I am done')
    evt.set()

def waiter():
    print('I am waiting')
    evt.wait()
    print('Finish waiting')

gevent.joinall([
        gevent.spawn(setter),
        gevent.spawn(waiter).spawn(waiter).spawn(waiter).spawn(waiter).spawn(waiter)
    ])

当调用Event.wait()函数时,协程会进入阻塞状态,直到调用set()函数后被唤醒

除了Event时间外,AsyncResult也有该功能,并且能在唤醒时传递数据

import gevent
from gevent.event import AsyncResult

aevt = AsyncResult()

def setter():
    print('Waitint for me')
    gevent.sleep(3)
    print('After 3 sec, I am done')
    aevt.set(value='Get Up')

def waiter():
    print('I am waiting')
    # 进入等待状态,同时等待接收数据
    # 如果没有传入数据,那么value值为None
    value = aevt.get()
    print('I get a value:',value)

gevent.joinall([
        gevent.spawn(setter),
        gevent.spawn(waiter).spawn(waiter)
    ])

6.信号量

信号量可以限制协程的数量

acquire()获取信号量

release()释放信号量

当所有的信号量都已经被获取后,剩下的协程只能等待某个释放才能继续执行

如果信号量的数量为1,那么久相当于同步锁

import gevent
from gevent.lock import BoundedSemaphore

sem = BoundedSemaphore(2)

def worker(r):
    sem.acquire()
    print(f'Worker {r} acquire a sem')
    gevent.sleep(2)
    sem.release()
    print(f'Worker {r} release a sem')

gevent.joinall([
        gevent.spawn(worker,i) for i in range(6)
    ])

7.协程本地变量

将变量放在local()中,即可将其作用域限制在该协程内,当其他协程想要访问另一协程的变量时就会报错

不同协程间可以有相同名称的变量,互不影响

import gevent
from gevent.local import local

data = local()

def f1():
    data.x = 'f1'
    print(data.x)

def f2():
    try:
        print(data.x)
    except:
        print('f2 has not data.x')

gevent.joinall([
        gevent.spawn(f1).spawn(f2)
    ])
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 协程 阅读目录 一 引子 二 协程介绍 三 Greenlet模块 四 Gevent模块 引子 之前我们学习了线程、...
    go以恒阅读 708评论 0 1
  • 进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。 多进程编程在python...
    hugoren阅读 4,958评论 1 4
  • 前述 进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。 多进程编程在pyt...
    softlns阅读 6,332评论 2 24
  • 一. 操作系统概念 操作系统位于底层硬件与应用软件之间的一层.工作方式: 向下管理硬件,向上提供接口.操作系统进行...
    月亮是我踢弯得阅读 5,965评论 3 28
  • 目录 一、开启线程的两种方式 在python中开启线程要导入threading,它与开启进程所需要导入的模块mul...
    CaiGuangyin阅读 2,400评论 1 16