Posted on 2014-02-13 16:41:10 gevent
摘要:hub是gevent中核心,依靠libev这个事件库,来调度所有的greenlet。
hub也是一个greenlet,就是所谓的’main’ greenlet。
hub会首先启动,然后马上启动事件循环。也就是libev的loop。
hub对应到实际的代码就是hub.py,其中最关键的是Hub类。
hub保存在线程的本地数据中,所以说每个线程中只存在一个’main’ hub。
从main greenlet切换到普通greenlet之后,main greenlet是停止执行的。greenlet是安排合理的串行,从而看起来像是并行。
切换到main greenlet是指首先切换到hub.wait,然后在greenlet.so中执行真正的greenlet切换。 即切换到需要运行的那个greenlet。
所谓的’由hub决定运行哪个greenlet’,实际上是loop检测到greenlet感兴趣的事件后,首先切换到hub,再由hub.switch切换到发生事件的那个greenlet。主导整个过程的是libev的loop。
Posted on 2014-01-22 12:35:42 gevent
除非有隐式或者显示的手段switch到这个greenlet。
只是创建了一个greenlet实例,并加入到了greenlet链,还没有运行它。
greenlet.switch最终会调用greenlet.c中的C函数。 greenlet.c中g_swith会扫描greenlet链,如果发现没有启动的greenlet,则调用g_initialstub启动它。 实际上g_initialstub会在greenlet类实例中查找run属性,找到了则运行它。 如果greenlet是已经启动的,则switch到main hub.
按照3的说法,它首先创建了greenlet实例,设置了实例的’run’属性。 然后直接运行刚才指定的’run’属性,也可以叫它callback,是一个callable对象。
最后再启动一个greenlet,为什么之前创建的greenlet还是可以运行?
因为最后运行的那个greenlet中,发生了switch,对应到greenlet.c中的g_swith。 它会扫描greenlet链,如果发现没有启动的greenlet则启动它们。 如果最后运行的那个greenlet中没有发生switch,则在它之前创建的greenlet无法运行。
例如下面的代码:
def g1():
print 'g1'
gevent.sleep(2)
def g2():
print 'g2'
gevent.sleep(2)
def g3():
print 'g3'
gevent.sleep(1)
gevent.spawn(g1)
gevent.spawn(g2)
gevent.spawn(g3).run()
输出:
g3
g1
g2
如果函数g3,没有最后一行gevent.sleep(1), 则g1和g2都不会执行 只是输出’g3’字符串之后,进程退出。
Waiter用来在greenlet之间通信,它简单包装了greenlet.swith。 典型的工作过程如下:
>>> result = Waiter() #1
>>> timer = get_hub().loop.timer(0.1) #2
>>> timer.start(result.switch, 'hello from Waiter') #3
>>> result.get() #4
'hello from Waiter'
#1: 创建一个Waiter类实例,在init中什么也不做。
#2: 启动一个libev的watcher(libev有许多watcher例如io/timer/signal等,可以理解为对特定对象的#监视对象#)
#3: 为这个#监视对象#设置callback和参数,这里的是Waiter实例的switch函数,参数为字符串。 接着立即让libev开始监视这个对象。 timer.start只是让libev启动一个time类型的watcher,并在事件发生后,执行回调。 waiter.switch的代码只有关键的一行:greenlet.switch(value),它切换当前协程到main hub. 但是watcher并没有执行result.switch,只是将它加入了libev的callback列表。 要等待libev.loop.run启动主循环。 记住,这里给libev的watcher传递了一个callback,并带有参数。
#4: 这里很关键, waiter.get中有self.hub.switch(),表示立即出让时间片到main hub. 流程到了hub.switch这里,它调用greenlet.switch(self)。 这里的greenlet是C代码中的greenlet类实例,是所有greenlet的父类。 所以我们到了greenlet.so模块内部。
注意这时,虽然main hub已经被创建了,但是从来没有运行过。 所以greenlet.c中switch会调用g_initialstub执行main hub的run函数,这里会启动core.so->loop.run,启动libev的 ioloop.
因为#3给libev的callback中增加了watcher(waiter.switch),所以ioloop启动后会检测watcher事件,事件发生后调用watcher的callback.
就会调用waiter.switch,其中一行代码:self.greenlet.switch(value) 因为我们一直在main hub中,所以又回到了hub.switch,在hub.switch中是return greenlet.switch(self),所以这里会返回。 流程最后又回到了wait.get中,其中returen hub.switch()返回最终的值。
结合Waiter是如何工作的,再看sleep的代码:
def sleep(seconds=0, ref=True):
# 得到main hub
hub = get_hub()
# 得到hub.loop,这是libev的主循环
loop = hub.loop
# 如果sleep时间小于等于0
# 启动一个waiter,直接调用loop的run_callback运行waiter.switch
# 再调用waiter.get()会立即返回
# 因为我们没有给core.so->loop传递任何watcher,所以会立即返回
if seconds <= 0:
waiter = Waiter()
loop.run_callback(waiter.switch)
waiter.get()
else:
# 这里启动了一个libev的timer类型的watcher
# 然后传递给hub.wait
# hub.wait的代码,是Waiter类的典型用法:
# waiter = Waiter()
# unique = object()
# watcher.start(waiter.switch, unique)
# try:
# result = waiter.get()
# assert result is unique, 'Invalid switch'
# finally:
# watcher.stop()
# 启动一个timer类型的watcher,
# 在waiter.get中切换到main hub,然后在事件发生后调用waiter.switch
# 在waiter.switch中,执行self.greenlet.switch,控制流又会回到waiter.get中
# 因为self.greenlet是调用waiter.get的协程,也就是调用gevent.sleep的协程
hub.wait(loop.timer(seconds, ref=ref))
首先monkey.patch_xxx只需要执行一次即可,即在main中执行.
其次monkey.patch_all会给所有模块做patch. 解决办法是,只使用特定模块的patch,如monkey.patch_socket
最后monkey.patch_xxx会给所有导入的模块做patch 解决办法是在其他需要导入的模块执行下面的操作:
import a
import socket
reload(socket)
print socket.socket # <class 'socket._socketobject'>