简体   繁体   English

无法跨多个进程使用多处理 BaseManager 服务器向/从已注册的共享 object 写入/读取值

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

So I have been having some troubles with the multiprocessing module, and namely with the BaseManager .所以我一直在使用multiprocessing模块,即BaseManager时遇到一些麻烦。

To reproduce the issue, the following four files can be written:要重现该问题,可以写入以下四个文件:

  1. The first file queue_manager.py just defines my BaseManager to be served, along with a convenience function to connect to the server.第一个文件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. The server logic itself with file server.py .服务器逻辑本身与文件server.py Here, we want to be able to serve two queues, named 'foo' and 'bar', which we could obtain across processes by calling the connect_to_manager() function defined before.在这里,我们希望能够为名为“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. The third file workers.py would be composed of the multiprocessing processes that should do some tasks on these queues.第三个文件workers.py将由应该在这些队列上执行某些任务的多处理进程组成。 Here, we only obtain the value in the queue and print it.这里,我们只获取队列中的值并打印出来。
# 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. Lastly, we create a client which places some values inside the queue with client.py最后,我们创建一个客户端,它使用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()

Let's say we run each of the last three files simultaneously in separate terminals (and in this order):假设我们在不同的终端中同时运行最后三个文件中的每一个(并按此顺序):

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

The issue is that the client successfully places values in the foo queue.问题是客户端成功地将值放入foo队列中。 HOWEVER, they are not picked up by the workers .然而,他们并没有被workers捡走。

Now one way to circumvent this is NOT to pass a lambda function to the QueueManager.register() call in the serve() function. Ie like this:现在避免这种情况的一种方法是不将 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:
    ....

instead of:代替:

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

However, I don't want to do this because I want to be able to dynamically create the queues/functions, since I don't know their names nor how many I want before I run.但是,我不想这样做,因为我希望能够动态创建队列/函数,因为在运行之前我不知道它们的名称,也不知道我想要多少。 Any Ideas?有任何想法吗?

First of all, in file workers.py method run , where you have references to variable input_queue , this needs to be changed to self.input_queue (see last listing).首先,在文件workers.py方法run中,您引用了变量input_queue ,这需要更改为self.input_queue (参见最后一个清单)。

I would then modify file file server.py to ensure that for a given queue name (such as foo , for example), that the QueueManager always serves up the same, singleton queue.Queue instance.然后我会修改文件server.py以确保对于给定的队列名称(例如foo ), QueueManager始终提供相同的singleton queue.Queue实例。 Once you do that as follows in this updated server.py file...在此更新的server.py文件中按如下方式执行此操作后...

Updated 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'])

... then the updated queue_manager.py file becomes simplified as: ...然后更新的queue_manager.py文件变得简化为:

Updated 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}

And for good measure, the corrected workers.py :为了更好地衡量,更正后的workers.py

Corrected 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