Python Gevent共享队列(侦听器进程)
我正在尝试使用一些代码,以便使用gevent实现登录到多线程程序。我想做的是设置自定义日志处理程序,将日志事件放入队列中,同时侦听器进程持续监视新的日志事件以进行适当处理。我过去曾使用多处理来实现这一点,但从未使用Gevent 我遇到了一个问题,程序陷入无限循环(侦听器进程),不允许其他线程“工作” 理想情况下,在工作进程完成后,我可以将任意值传递给侦听器进程,让它中断循环,然后将所有进程连接在一起。以下是我目前掌握的情况:Python Gevent共享队列(侦听器进程),python,logging,queue,gevent,Python,Logging,Queue,Gevent,我正在尝试使用一些代码,以便使用gevent实现登录到多线程程序。我想做的是设置自定义日志处理程序,将日志事件放入队列中,同时侦听器进程持续监视新的日志事件以进行适当处理。我过去曾使用多处理来实现这一点,但从未使用Gevent 我遇到了一个问题,程序陷入无限循环(侦听器进程),不允许其他线程“工作” 理想情况下,在工作进程完成后,我可以将任意值传递给侦听器进程,让它中断循环,然后将所有进程连接在一起。以下是我目前掌握的情况: import gevent from gevent.pool impo
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
从gevent.pool导入池
导入队列
随机输入
导入时间
def侦听器(q):
尽管如此:
如果不是q.empty():
num=q.get()
打印“号码为:%s”%num
如果num则第一个问题是,如果队列最初为空,则侦听器永远不会让步。您生成的第一个任务是侦听器。当它开始时,有一个,而True:
,q将是空的,因此您转到else分支,它只是继续,循环回到while循环的开始,然后q仍然是空的。因此,您只需坐在第一个线程中,不断检查q是否为空
这里的关键是gevent不使用“本机”线程或进程。与“真实”线程不同的是,gevent使用“greenlets”,它要求您对另一个任务进行“屈服控制”,而“真实”线程可以在任何时候通过幕后的东西(比如您的操作系统调度程序)切换到“真实”线程。gevent认为任何东西都会阻塞,例如从网络、磁盘读取,或者使用阻塞gevent操作之一
一个粗略的修复方法是在pid==9而不是0时启动侦听器。通过使其最后生成,q中将有项目,并且它将进入主if分支。缺点是这并不能解决逻辑问题,所以当队列第一次为空时,您将再次陷入无限循环
更正确的修复方法是放置gevent.sleep()
,而不是continue
。睡眠是一个阻塞操作,因此您的其他任务将有机会运行。在没有参数的情况下,它不会等待任何时间,但仍然让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将切换到要运行的其他任务之一,从而避免了示例代码中的无限循环问题
由于此版本使用队列
作为for循环迭代器,因此还可以自动在队列中添加一个很好的sentinel值,以使侦听器任务退出。如果for循环从其迭代器中获得StopIteration
,它将干净地退出。因此,当从q读取的for循环从q获取StopIteration
时,它退出,然后函数退出,生成的任务完成