13

我正在玩 gevent,我试图了解为什么我的代码被阻塞以及如何修复它。

我有一个 greenlets 池,它们每个都与一个从远程 thrift 服务器收集数据的 thrift 客户端交谈。出于练习的目的,thrift 服务器总是需要 > 1s 来返回任何数据。当我生成 greenlets 并运行 join 时,它们不会并行执行,而是一个接一个地执行。我的理解是,发生这种情况是因为我的代码是“阻塞的”,因为当我运行时monkey.patch_all(),所有 greenlet 都神奇地并行运行。

那么如何让代码不阻塞自己,而不是让猴子修补所有东西而不理解它在做什么呢?

这里有一个我不明白的例子:

import time

from gevent.pool import Pool

def hello():
    print 'Hello %d' % time.time()
    time.sleep(1) 

def main():
    pool = Pool(5)
    for _ in xrange(5):
        pool.spawn(hello)

    pool.join()

if __name__ == '__main__':
    main()

输出

Hello 1345477112
Hello 1345477113
Hello 1345477114
Hello 1345477115
Hello 1345477116

我知道我可以使用 gevent.sleep,但是如何使用常规 time.sleep 使该功能非阻塞?

谢谢

4

1 回答 1

17

Greenlets 从不并行运行,它们都共享同一个进程和同一个线程,因此,一次最多运行一个。

Greenlets是绿色的,因为它们是co-routines(“co”来自于合作),因此,甚至不能说它们是并发运行的,因为你需要协调它们的运行。Gevent 在幕后为您完成大部分工作,并从 libevent(或 libev)知道哪些 greenlets 已准备好运行。根本没有抢占。

在您给出的示例中,time.sleep(2)将使进程在操作系统内休眠,因此 gevent 的调度程序将无法运行并且无法切换到另一个 greenlet。

因此,关于您的问题:如果您不想修补现有代码,则必须手动替换对 gevent 等效项的每个阻塞调用,以便 gevent 可以安排调用 greenlet 并选择另一个运行。

编辑:关于使用 gevent 和节俭而不用猴子修补所有:我不知道它是否值得。

如果要修改 (fork) thrift 的库,只需要更改文件TSocket.py,并更改:

import socket

到:

from gevent import socket

但是你的 thrift 库将依赖于 gevent,如果你更新了 thrift,你将需要重新应用补丁。

您也可以子类TSocket化,更改方法open()以使用 gevent 的套接字,并使用它来代替前者,但对我来说似乎更复杂。

我实际上是在使用 Thrift 和 Gevent,为了简单起见,我选择猴子修补整个事情。

于 2012-08-20T16:00:37.370 回答