从Gevent循环回调中的阻塞源获取数据

时间:2012-08-06 10:32:54

标签: python gevent pycurl

我正在尝试使用Pycurl和Gevent来执行HTTP上传。为此,我依赖于geventcurl.py模块,该模块改变了libcurl的 Multi API以使用Gevent的事件循环。

问题在于 READFUNCTION 回调。此回调在 HUB 上下文中执行,因此我们不能 wait(),但此回调必须返回要上传的数据,在我的情况下,此数据来自阻塞源。

以下是一段演示此问题的代码:

#!/usr/bin/env python
from gevent import monkey; monkey.patch_all()
import gevent
from gevent.queue import Queue
import pycurl
from geventcurl import Curl

URL = 'http://localhost:8000/'

class QueueReader:
    def __init__(self, q):
        self.q = q
    def read_callback(self, size):
        return self.q.get(timeout=10)

dataq = Queue(10)
c = Curl()
c.setopt(pycurl.URL, URL)
c.setopt(pycurl.UPLOAD, 1)
c.setopt(pycurl.READFUNCTION, QueueReader(dataq).read_callback)

# Start transfer
g = gevent.spawn(c.perform)
for i in xrange(10):
    dataq.put(str(i))
    gevent.sleep(1)
g.join()
c.close()

要运行代码段,您只需要在localhost:8000上监听,nc -l 8000即可。会发生什么,因为read_callback()在HUB上下文中执行,它不会等待,如果队列为空,它将立即引发Empy异常。使用AsyncResult也没有用,因为我们必须wait()才能得到结果。

有没有办法从事件循环回调中的可能阻塞源获取数据?

1 个答案:

答案 0 :(得分:0)

如果你摆脱timeout=10,它将等待来自阻止源的数据。 (参见:http://www.gevent.org/gevent.queue.html,默认行为。)

如果read_callback()的阻塞会干扰for i in xrange(10)循环的操作,那么通过例如gevent.spawn()将该循环放入其自己的greenlet中。

另请注意,默认情况下put()将阻止队列已满。请考虑put_nowait()put(timeout=...)put(block=False)来更改默认行为。