显式switch()与gevent

时间:2015-08-03 10:30:35

标签: python gevent greenlets

我有一个在gevent中运行的原始生产者/消费者脚本。它启动了一些将事物放入gevent.queue.Queue的生成器函数,以及一个将它们再次从队列中取出的消费者函数:

from __future__ import print_function

import time

import gevent
import gevent.queue
import gevent.monkey

q = gevent.queue.Queue()

# define and spawn a consumer
def consumer():
    while True:
        item = q.get(block=True)
        print('consumer got {}'.format(item))

consumer_greenlet = gevent.spawn(consumer)

# define and spawn a few producers
def producer(ID):
    while True:
        print("producer {} about to put".format(ID))
        q.put('something from {}'.format(ID))
        time.sleep(0.1)
#       consumer_greenlet.switch()      

producer_greenlets = [gevent.spawn(producer, i) for i in range(5)]

# wait indefinitely
gevent.monkey.patch_all()
print("about to join")
consumer_greenlet.join()

如果我让gevent隐式处理调度(例如通过调用time.sleep或其他gevent.monkey.patch() ed函数),它工作正常,但是当我明确切换到使用者时(用time.sleep替换{注释掉switch调用),gevent引发了一个AssertionError:

Traceback (most recent call last):
  File "/my/virtualenvs/venv/local/lib/python2.7/site-packages/gevent/greenlet.py", line 327, in run
    result = self._run(*self.args, **self.kwargs)
  File "switch_test.py", line 14, in consumer
    item = q.get(block=True)
  File "/my/virtualenvs/venv/lib/python2.7/site-packages/gevent/queue.py", line 201, in get
    assert result is waiter, 'Invalid switch into Queue.get: %r' % (result, )
AssertionError: Invalid switch into Queue.get: ()
<Greenlet at 0x7fde6fa6c870: consumer> failed with AssertionError

我想使用显式切换,因为在生产中我有很多生产者,gevent的调度不会为消费者分配足够的运行时间并且队列变得越来越长(这很糟糕)。或者,非常感谢任何有关如何配置或修改gevent的调度程序的见解。

这是在Python 2.7.2,gevent 1.0.1和greenlet 0.4.5。

1 个答案:

答案 0 :(得分:0)

对我而言,显式切换对隐式切换并不是很好。 您已经发生了隐式切换,因为猴子修补了I / O或者因为gevent.queue.Queue()

gevent文档不鼓励使用原始greenlet方法:

  

作为一个greenlet子类,Greenlet也有switch()和throw()   方法。但是,这些不应该在应用程序级别使用   他们可以很容易地导致永远不定期的greenlets。   更喜欢更高级别的安全类,比如Event和Queue。

迭代gevent.queue.Queue()或访问队列的get方法进行隐式切换,有趣的是put没有。所以你必须自己生成一个隐式线程切换。最简单的方法是致电gevent.sleep(0)(您无需等待特定时间)。

总而言之,您甚至不必使用虚假的东西,只要您的代码没有阻止IO操作。

我会像这样重写你的代码:

import gevent
import gevent.queue

q = gevent.queue.Queue()

# define and spawn a consumer
def consumer():
    for item in q:
        print('consumer got {}'.format(item))

consumer_greenlet = gevent.spawn(consumer)

# define and spawn a few producers
def producer(ID):
    print('producer started', ID)
    while True:
        print("producer {} about to put".format(ID))
        q.put('something from {}'.format(ID))
        gevent.sleep(0)

producer_greenlets = [gevent.spawn(producer, i) for i in range(5)]
# wait indefinitely
print("about to join")
consumer_greenlet.join()