我试图让一些代码工作,我可以使用gevent实现登录到多线程程序。我想要做的是设置自定义日志处理程序以将日志事件放入队列,而侦听器进程持续监视新日志事件以便适当处理。我以前使用Multiprocessing完成了这项工作,但从未使用过Gevent。
我遇到的问题是程序陷入无限循环(监听程序进程),而不允许其他线程进行工作" ...
理想情况下,在工作进程完成后,我可以将任意值传递给侦听器进程,告诉它打破循环,然后将所有进程连接在一起。这就是我到目前为止所拥有的:
import gevent
from gevent.pool import Pool
import Queue
import random
import time
def listener(q):
while True:
if not q.empty():
num = q.get()
print "The number is: %s" % num
if num <= 100:
print q.get()
# got passed 101, break out
else:
break
else:
continue
def worker(pid,q):
if pid == 0:
listener(q)
else:
gevent.sleep(random.randint(0,2)*0.001)
num = random.randint(1,100)
q.put(num)
def main():
q = Queue.Queue()
all_threads = []
all_threads = [gevent.spawn(worker, pid,q) for pid in xrange(10)]
gevent.wait(all_threads[1:])
q.put(101)
gevent.joinall(all_threads)
if __name__ == '__main__':
main()
正如我所说,该程序似乎在第一个过程中被挂起,并且不允许其他工作人员做他们的事情。我也尝试完全单独生成监听程序进程(这实际上我更愿意这样做),但这似乎没有用,所以我尝试了这种方式。
任何帮助都会受到赞赏,感觉我可能只是错过了关于gevent后端的明显信息。
由于
答案 0 :(得分:1)
第一个问题是如果队列最初为空,那么你的监听器永远不会产生。你产生的第一个任务是你的听众。当它开始时,有while True:
,q将为空,所以你去了else分支,它只是继续,循环回到while循环的开始,然后q仍然是空的。所以你只是坐在第一个线程中不断检查q是空的。
这里的关键是gevent不使用&#34; native&#34;线程或进程。不像&#34;真实&#34;线程,可以在任何时候通过幕后的东西(如你的操作系统调度程序)切换到,gevent使用&greenlets&#39;这需要你做一些事情来产生控制&#34;另一项任务。这是gevent认为会阻止的东西,例如从网络,磁盘读取或使用其中一个阻塞gevent操作。
一个原始修复是在pid == 9
而不是0时启动你的监听器。通过使它最后生成,q中将有项目,它将进入主if分支。缺点是这并没有解决逻辑问题,所以第一次排队时,你会再次陷入无限循环。
更正确的解决方法是放置gevent.sleep()
而不是continue
。 sleep是一个阻塞操作,所以你的其他任务将有机会运行。没有参数,它等待没有时间,但仍然让gevent有机会决定切换到另一个任务,如果它准备好运行。但是,这仍然不是非常有效,就好像队列是空的一样,它将花费大量无意义的时间一遍又一遍地检查,并要求尽快再次运行。睡眠时间超过默认值0将更有效,但会延迟处理您的日志消息。
但是,您可以利用以下事实:许多gevent类型(如Queue)可以以更多Pythonic方式使用,并使您的代码更简单,更易于理解,以及更多高效。
import gevent
from gevent.queue import Queue
def listener(q):
for msg in q:
print "the number is %d" % msg
def worker(pid,q):
gevent.sleep(random.randint(0,2)*0.001)
num = random.randint(1,100)
q.put(num)
def main():
q = Queue()
listener_task = gevent.spawn(listener, q)
worker_tasks = [gevent.spawn(worker, pid, q) for pid in xrange(1, 10)]
gevent.wait(worker_tasks)
q.put(StopIteration)
gevent.join(listener_task)
这里,Queue
可以作为for
循环中的迭代器运行。只要有消息,就会获得一个项目,运行循环,然后等待另一个项目。如果没有物品,它将会阻挡并徘徊,直到下一个物品到达。但是,由于它会阻塞,gevent会切换到你要运行的其他任务之一,避免了你的示例代码所带来的无限循环问题。
因为这个版本使用Queue
作为for循环迭代器,所以我们也可以自动将一个很好的sentinel值放入队列中以使侦听器任务退出。如果for循环从其迭代器获得StopIteration
,它将完全退出。因此,当我从q读取的for循环从q获得StopIteration
时,它退出,然后函数退出,并且生成的任务完成。