簡體   English   中英

python多處理的生產者/消費者問題

[英]producer/consumer problem with 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 服務器,一旦收到用戶的請求,它就會將任務放入隊列中。 好像隊列中有新任務的時候消費者進程還是被阻塞了,這很奇怪。

PS另外兩個與上述無關的問題,我不確定將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()

我認為 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_PROCESSES):
            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

“第二個問題,優雅地停止 HTTP 服務器的最佳方式是什么?”

這很難。

進程間通信有兩種選擇:

  • 帶外控制。 服務器具有另一種通信機制。 另一個套接字、Unix 信號或其他東西。 其他內容可能是服務器本地目錄中的“立即停止”文件。 看起來很奇怪,但它確實工作得很好,而且比引入一個選擇循環來偵聽多個套接字或一個信號處理程序來捕獲 Unis 信號更簡單。

    “立即停止”文件很容易實現。 evwsgi.run()循環僅在每次請求后檢查此文件。 為了讓服務器停止,你創建文件,執行一個/control請求(它會得到一個 500 錯誤或者其他的東西,這並不重要)並且服務器應該會停止。 記得刪除 stop-now 文件,否則你的服務器不會重啟。

  • 帶內控制。 服務器有另一個 URL ( /stop ) 將停止它。 從表面上看,這似乎是一場安全噩夢,但這完全取決於該服務器的使用地點和方式。 由於它似乎是內部請求隊列的簡單包裝器,因此這個額外的 URL 運行良好。

    為了使這個工作,你需要編寫你自己的evwsgi.run()版本,可以通過設置一些變量來終止循環。

編輯

您可能不想終止您的服務器,因為您不知道它的工作線程的狀態。 您需要向服務器發送信號,然后您只需等待它正常完成操作。

如果你想強行殺死服務器,那么os.kill() (或multiprocessing.terminate )將起作用。 當然,除了您不知道子線程在做什么之外。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM