greenlet instances 之间的关系存在两种:
- 仅有包含于 greenlet instances 集合的关系
- 同步关系,即存在协作关系
第一种形式很常见,不同的 greenlet instance 之间没有交流,且没有共享数据需要进行操作,各自做各自的事情。
对于第二种形式,gevent 提供了几种数据结构便于 greenlet instances 间进行同步。
Gevent常用数据结构和多进程/线程类似,有以下7种:
- 事件
- 队列
- 组和池
- 锁和信号量
- 线程局部变量
- 子进程
- Actors
1,事件
事件(event)是一个在Greenlet之间异步通信的形式。
gevent.event.Event
通过这种同步原语可以控制一个 greenlet 与多个 greenlet 通过 flag 的 True/False 值进行同步,Event instance 的初始 flag 是 False.但这种同步是 一次性的,即 set() 设置 flag 为 True 对之前因为 wait() 阻塞的 greenlets 都有效,但对 set() 之后再次设置wait() 的 greenlet 无效。
一般的使用场景是:一个 greenlet 控制何时通过 Event().set() 方法设置 flag 的值为 True 来控制其它协作的 greenlet 运行,有协作关系的 greenlet 通过 Event().wait() 方法等待 flag 被设为 True,否则一直阻塞。
代码示例:
import gevent
from gevent.event import Event
'''
Illustrates the use of events
'''
evt = Event()
def setter():
'''After 3 seconds, wake all threads waiting on the value of evt'''
print('厨师开始做菜')
gevent.sleep(3)
print("菜做好了")
evt.set() #设置event的flag为True,所有wait的greenlet解除阻塞
def waiter(name):
'''After 3 seconds the get call will unblock'''
print("{} 服务员等菜".format(name))
evt.wait() # 阻塞,跳转到下一个greenlet
print("菜好了, {} 送餐".format(name))
def main():
gevent.joinall([
gevent.spawn(setter),
gevent.spawn(waiter, "服务员 A"),
gevent.spawn(waiter, "服务员 B"),
])
if __name__ == '__main__':
main()
运行结果:
厨师开始做菜
服务员 A 服务员等菜
服务员 B 服务员等菜
菜做好了
菜好了, 服务员 A 送餐
菜好了, 服务员 B 送餐
这里setter和waiter一共起了三个协程。分析一下运行顺序应该很容易了解evt是干嘛的:
首先回调之行到运行setter 打印str然后gevent.sleep(3);
然后执行第二个回调waitter()执行到evt.wait()的时候阻塞住然后切换到下一个greenlet实例,执行第三个回调waitter()执行到evt.wait()又被阻塞了,这个时候继续执行下一个回调就会回到setter里面,因为没有人在他前面往hub.loop里注册了;
之后这里执行【print("菜做好了")】,运行evt.set()将flag设置为True。
然后另外两个被阻塞的waitter的evt.wait()方法在看到flag已经为True之后不再继续阻塞运行并且结束。
可以看到,Event可以协同好几个Greenlet同时工作,并且一个主Greenlet在操作的时候可以让其他几个都处于等待的状态,可以实现一些特定的环境和需求。
注意:
一旦 flag 被设为了 True,之前所有的Event().wait() 操作将不再阻塞,这些 greenlet 将等待 hub greenlet 进行调度,即使之后 _flag 又被设置为了 False。可参考这个 问题,还可以再对比一个程序理解这点儿,参考 这个。
若出现 wait() 处于永远的阻塞状态,即在 wait() 设置之后,在其它 greenlet 中没有 set() 操作,则 gevent 会抛出这样的异常。
示例代码:
import random
import gevent
from gevent.event import Event
class TestEvent(object):
def __init__(self):
self.event = Event()
def run(self):
producers = [gevent.spawn(self._producer, i) for i in range(3)]
consumers = [gevent.spawn(self._consumer, i) for i in range(3)]
tasks = []
tasks.extend(producers)
tasks.extend(consumers)
gevent.joinall(tasks)
def _producer(self, pid):
print("I'm producer %d and now I don't want consume to do something" % (pid,))
self.event.clear()
sleeptime = random.randint(0, 5) * 0.01
print("Sleeping time is %f" % (sleeptime,))
gevent.sleep(sleeptime)
print("I'm producer %d and now consumer could do something." % (pid,))
self.event.set()
def _consumer(self, pid):
print("I'm consumer %d and now I'm waiting for producer" % (pid,))
gevent.sleep(random.randint(0, 10) * 0.1)
flag = self.event.wait()
print("I'm consumer %d. Flag is %r and now I can do something" % (pid, flag))
self.event.clear()
if __name__ == '__main__':
test = TestEvent()
test.run()
执行结果:
I'm producer 0 and now I don't want consume to do something
Sleeping time is 0.020000
I'm producer 1 and now I don't want consume to do something
Sleeping time is 0.000000
I'm producer 2 and now I don't want consume to do something
Sleeping time is 0.020000
I'm consumer 0 and now I'm waiting for producer
I'm consumer 1 and now I'm waiting for producer
I'm consumer 2 and now I'm waiting for producer
I'm producer 1 and now consumer could do something.
I'm producer 0 and now consumer could do something.
I'm producer 2 and now consumer could do something.
I'm consumer 2. Flag is True and now I can do somet