python多处理的生产者/消费者问题

时间:2009-05-27 09:28:57

标签: python multiprocessing

我正在编写一个服务器程序,其中包含一个生产者和多个消费者, 令我困惑的只是生产者放入队列的第一个任务 消耗,然后排队的任务不再被消耗,他们仍然存在 在队列中永远。

from multiprocessing import Process, Queue, cpu_count
from http import httpserv
import time

def work(queue):
    while True:
        task = queue.get()
        if task is None:
            break
        time.sleep(5)
        print "task done:", task
    queue.put(None)

class Manager:
    def __init__(self):
        self.queue = Queue()
        self.NUMBER_OF_PROCESSES = cpu_count()

    def start(self):
        self.workers = [Process(target=work, args=(self.queue,))
                        for i in xrange(self.NUMBER_OF_PROCESSES)]
        for w in self.workers:
            w.start()

        httpserv(self.queue)

    def stop(self):
        self.queue.put(None)
        for i in range(self.NUMBER_OF_PROCESSES):
            self.workers[i].join()
        queue.close()

Manager().start()

生产者是一个HTTP服务器,一旦接收就将任务放入队列中 来自用户的请求。似乎消费者流程仍然存在 当队列中有新任务时被阻止,这很奇怪。

P.S。另外两个与上述无关的问题,我不确定是否 最好将HTTP服务器放在除主服务器之外的其他进程中 进程,如果是,我怎么能让主进程继续运行 儿童过程结束。第二个问题,什么是最好的方法来阻止 HTTP服务器优雅吗?

编辑:添加生产者代码,它只是一个简单的python wsgi服务器:

import fapws._evwsgi as evwsgi
from fapws import base

def httpserv(queue):
    evwsgi.start("0.0.0.0", 8080)
    evwsgi.set_base_module(base)

    def request_1(environ, start_response):
        start_response('200 OK', [('Content-Type','text/html')])
        queue.put('task_1')
        return ["request 1!"]

    def request_2(environ, start_response):
        start_response('200 OK', [('Content-Type','text/html')])
        queue.put('task_2')
        return ["request 2!!"]

    evwsgi.wsgi_cb(("/request_1", request_1))
    evwsgi.wsgi_cb(("/request_2", request_2))

    evwsgi.run()

3 个答案:

答案 0 :(得分:10)

我认为Web服务器部分肯定存在问题,因为这非常有效:

from multiprocessing import Process, Queue, cpu_count
import random
import time


def serve(queue):
    works = ["task_1", "task_2"]
    while True:
        time.sleep(0.01)
        queue.put(random.choice(works))


def work(id, queue):
    while True:
        task = queue.get()
        if task is None:
            break
        time.sleep(0.05)
        print "%d task:" % id, task
    queue.put(None)


class Manager:
    def __init__(self):
        self.queue = Queue()
        self.NUMBER_OF_PROCESSES = cpu_count()

    def start(self):
        print "starting %d workers" % self.NUMBER_OF_PROCESSES
        self.workers = [Process(target=work, args=(i, self.queue,))
                        for i in xrange(self.NUMBER_OF_PROCESSES)]
        for w in self.workers:
            w.start()

        serve(self.queue)

    def stop(self):
        self.queue.put(None)
        for i in range(self.NUMBER_OF_PROCESS):
            self.workers[i].join()
        self.queue.close()


Manager().start()

示例输出:

starting 2 workers
0 task: task_1
1 task: task_2
0 task: task_2
1 task: task_1
0 task: task_1

答案 1 :(得分:4)

“第二个问题,优雅地停止HTTP服务器的最佳方法是什么?”

这很难。

进程间通信有两种选择:

  • 带外控制。服务器有另一种通信机制。另一个套接字,Unix信号或其他东西。其他东西可能是服务器本地目录中的“stop-now”文件。看起来很奇怪,但它确实运行良好,并且比引入选择循环来监听多个套接字或信号处理程序以捕获Unis信号更简单。

    “stop-now”文件易于实现。 evwsgi.run()循环仅在每次请求后检查此文件。要使服务器停止,您需要创建文件,执行/control请求(这将获得500错误或其他内容,这并不重要),服务器应该停止运行。请记住删除stop-now文件,否则服务器将不会重新启动。

  • 带内控制。服务器有另一个URL(/stop),它将停止它。从表面上看,这似乎是一场安全噩梦,但它完全取决于该服务器的使用位置和方式。由于它似乎是一个围绕内部请求队列的简单包装器,因此这个额外的URL运行良好。

    为了完成这项工作,您需要编写自己的evwsgi.run()版本,可以通过设置某个变量以终止循环来终止。

修改

您可能不想终止服务器,因为您不知道它的工作线程的状态。您需要向服务器发出信号,然后您必须等到它正常完成。

如果您想强制终止服务器,则os.kill()(或multiprocessing.terminate)将起作用。当然,除了你不知道子线程在做什么之外。

答案 2 :(得分:1)