簡體   English   中英

無法跨多個進程使用多處理 BaseManager 服務器向/從已注冊的共享 object 寫入/讀取值

[英]Unable to write/read value to/from registered shared object with multiprocessing BaseManager server across multiple processes

所以我一直在使用multiprocessing模塊,即BaseManager時遇到一些麻煩。

要重現該問題,可以寫入以下四個文件:

  1. 第一個文件queue_manager.py只是定義了我要服務的 BaseManager,以及連接到服務器的便利 function。
# queue_manager.py

import multiprocessing
from multiprocessing.managers import BaseManager

ADDRESS = "127.0.0.1"
PORT = 50000
PASSWORD = "password"

class QueueManager(BaseManager):
    pass

def connect_to_manager(names):
    for name in names:
        QueueManager.register(name + "_input_tasks")

    manager = QueueManager(address=(ADDRESS, PORT), authkey=PASSWORD.encode('utf-8'))
    manager.connect()
    return multiprocessing.Manager().dict({name: getattr(manager, name + "_input_tasks")() for name in names})

  1. 服務器邏輯本身與文件server.py 在這里,我們希望能夠為名為“foo”和“bar”的兩個隊列提供服務,我們可以通過調用之前定義的connect_to_manager() function 跨進程獲取它們。
# server.py

from queue_manager import QueueManager, ADDRESS, PORT, PASSWORD
import queue


class Server:

    @staticmethod
    def serve(names):
        print("Serving...")
        for name in names:
            QueueManager.register(name + "_input_tasks", callable=lambda: queue.Queue())

        m = QueueManager(address=(ADDRESS, PORT), authkey=PASSWORD.encode('utf-8'))
        s = m.get_server()
        s.serve_forever()


def main(names):
    Server.serve(names)

if __name__ == '__main__':
    main(['foo', 'bar'])

  1. 第三個文件workers.py將由應該在這些隊列上執行某些任務的多處理進程組成。 這里,我們只獲取隊列中的值並打印出來。
# workers.py

import multiprocessing
from queue_manager import connect_to_manager, PASSWORD
import time

class MyProcess(multiprocessing.Process):
    def __init__(self, input_queue):
        multiprocessing.Process.__init__(self)
        self.input_queue = input_queue

    def run(self):
        multiprocessing.current_process().authkey = PASSWORD.encode('utf-8')
        while True:
            time.sleep(1)
            print("Running...")
            if input_queue.empty():
                print("Empty")
            else:
                x = input_queue.get()
                print(x) 

def create_workers(input_queue, n_workers):
    p = MyProcess(input_queue)
    p.start()
    p.join()


if __name__ == '__main__':
    multiprocessing.current_process().authkey = PASSWORD.encode('utf-8')
    name_queue_mapping = connect_to_manager(['foo', 'bar'])
    
    # assume we focus on foo queue
    input_queue = name_queue_mapping['foo']
    create_workers(input_queue, 1)
  1. 最后,我們創建一個客戶端,它使用client.py將一些值放入隊列中
# client.py

from queue_manager import connect_to_manager, PASSWORD
import multiprocessing
import time

def client():
    multiprocessing.current_process().authkey = PASSWORD.encode('utf-8')
    names = ['foo', 'bar']
    name_queue_mapping = connect_to_manager(names)
    foo_queue = name_queue_mapping["foo"]
    bar_queue = name_queue_mapping["bar"]


    for i in range(5):
        time.sleep(1)
        foo_queue.put(i)


if __name__ == '__main__':
    client()

假設我們在不同的終端中同時運行最后三個文件中的每一個(並按此順序):

  • python server.py
  • python client.py
  • python workers.py

問題是客戶端成功地將值放入foo隊列中。 然而,他們並沒有被workers撿走。

現在避免這種情況的一種方法是不將 lambda function 傳遞給serve() function 中的QueueManager.register()調用。即像這樣:

QueueManager.register(name + "_input_tasks", callable=get_foo_queue)

# where get_foo_queue is defined globally in the server.py file as such

# server.py
my_foo_queue = queue.Queue()

def get_foo_queue():
    return my_foo_queue

class Server:
    ....

代替:

QueueManager.register(name + "_input_tasks", callable=lambda: queue.Queue())

但是,我不想這樣做,因為我希望能夠動態創建隊列/函數,因為在運行之前我不知道它們的名稱,也不知道我想要多少。 有任何想法嗎?

首先,在文件workers.py方法run中,您引用了變量input_queue ,這需要更改為self.input_queue (參見最后一個清單)。

然后我會修改文件server.py以確保對於給定的隊列名稱(例如foo ), QueueManager始終提供相同的singleton queue.Queue實例。 在此更新的server.py文件中按如下方式執行此操作后...

更新了 server.py

# server.py

from queue_manager import QueueManager, ADDRESS, PORT, PASSWORD
import queue

queues = {}

def get_queue(queue_name):
    def f():
        return queues[queue_name]
    return f

class Server:

    @staticmethod
    def serve(names):
        print("Serving...")
        for name in names:
            queue_name = name + "_input_tasks"
            q = queue.Queue()
            queues[queue_name] = q
            callable = get_queue(queue_name)
            QueueManager.register(queue_name, callable)

        m = QueueManager(address=(ADDRESS, PORT), authkey=PASSWORD.encode('utf-8'))
        s = m.get_server()
        s.serve_forever()


def main(names):
    Server.serve(names)

if __name__ == '__main__':
    main(['foo', 'bar'])

...然后更新的queue_manager.py文件變得簡化為:

更新了 queue_manager.py

# queue_manager.py

import multiprocessing
from multiprocessing.managers import BaseManager

ADDRESS = "127.0.0.1"
PORT = 50000
PASSWORD = "password"

class QueueManager(BaseManager):
    pass

def connect_to_manager(names):
    for name in names:
        QueueManager.register(name + "_input_tasks")

    manager = QueueManager(address=(ADDRESS, PORT), authkey=PASSWORD.encode('utf-8'))
    manager.connect()
    return {name: getattr(manager, name + "_input_tasks")() for name in names}

為了更好地衡量,更正后的workers.py

更正 workers.py

# workers.py

import multiprocessing
from queue_manager import connect_to_manager, PASSWORD
import time

class MyProcess(multiprocessing.Process):
    def __init__(self, input_queue):
        multiprocessing.Process.__init__(self)
        self.input_queue = input_queue

    def run(self):
        multiprocessing.current_process().authkey = PASSWORD.encode('utf-8')
        while True:
            time.sleep(1)
            print("Running...")
            if self.input_queue.empty():
                print("Empty")
            else:
                x = self.input_queue.get()
                print(x)

def create_workers(input_queue, n_workers):
    p = MyProcess(input_queue)
    p.start()
    p.join()


if __name__ == '__main__':
    multiprocessing.current_process().authkey = PASSWORD.encode('utf-8')
    name_queue_mapping = connect_to_manager(['foo', 'bar'])

    # assume we focus on foo queue
    input_queue = name_queue_mapping['foo']
    create_workers(input_queue, 1)

暫無
暫無

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

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