繁体   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