วิธีใช้คิวการประมวลผลหลายขั้นตอนใน Python


94

ฉันมีปัญหามากในการพยายามทำความเข้าใจว่าคิวการประมวลผลหลายตัวทำงานบน python อย่างไรและจะใช้งานอย่างไร สมมติว่าฉันมีโมดูล python สองโมดูลที่เข้าถึงข้อมูลจากไฟล์ที่แชร์ลองเรียกโมดูลทั้งสองนี้ว่าตัวเขียนและผู้อ่าน แผนของฉันคือให้ทั้งผู้อ่านและผู้เขียนใส่คำขอลงในคิวการประมวลผลหลายขั้นตอนแยกกันสองคิวจากนั้นให้กระบวนการที่สามแสดงคำขอเหล่านี้ในลูปและดำเนินการดังกล่าว

ปัญหาหลักของฉันคือฉันไม่รู้วิธีใช้ multiprocessing.queue อย่างถูกต้องคุณไม่สามารถสร้างอินสแตนซ์ออบเจ็กต์สำหรับแต่ละกระบวนการได้เนื่องจากจะเป็นคิวแยกกันคุณจะแน่ใจได้อย่างไรว่ากระบวนการทั้งหมดเกี่ยวข้องกับคิวที่ใช้ร่วมกัน (หรือ ในกรณีนี้คือคิว)


4
ส่งผ่านคิวไปยังแต่ละคลาสของกระบวนการเป็นพารามิเตอร์เมื่อคุณสร้างอินสแตนซ์ในกระบวนการหลัก
Joel Cornett

คำตอบ:


122

ปัญหาหลักของฉันคือฉันไม่รู้วิธีใช้ multiprocessing.queue อย่างถูกต้องคุณไม่สามารถสร้างอินสแตนซ์ออบเจ็กต์สำหรับแต่ละกระบวนการได้เนื่องจากจะเป็นคิวแยกกันคุณจะแน่ใจได้อย่างไรว่ากระบวนการทั้งหมดเกี่ยวข้องกับคิวที่ใช้ร่วมกัน (หรือ ในกรณีนี้คือคิว)

นี่เป็นตัวอย่างง่ายๆของผู้อ่านและนักเขียนที่แบ่งปันคิวเดียว ... ผู้เขียนส่งจำนวนเต็มไปยังผู้อ่าน เมื่อนักเขียนหมดตัวเลขจะส่ง 'DONE' ซึ่งช่วยให้ผู้อ่านรู้ว่าจะแยกออกจากวงอ่าน

from multiprocessing import Process, Queue
import time
import sys

def reader_proc(queue):
    ## Read from the queue; this will be spawned as a separate Process
    while True:
        msg = queue.get()         # Read from the queue and do nothing
        if (msg == 'DONE'):
            break

def writer(count, queue):
    ## Write to the queue
    for ii in range(0, count):
        queue.put(ii)             # Write 'count' numbers into the queue
    queue.put('DONE')

if __name__=='__main__':
    pqueue = Queue() # writer() writes to pqueue from _this_ process
    for count in [10**4, 10**5, 10**6]:             
        ### reader_proc() reads from pqueue as a separate process
        reader_p = Process(target=reader_proc, args=((pqueue),))
        reader_p.daemon = True
        reader_p.start()        # Launch reader_proc() as a separate python process

        _start = time.time()
        writer(count, pqueue)    # Send a lot of stuff to reader()
        reader_p.join()         # Wait for the reader to finish
        print("Sending {0} numbers to Queue() took {1} seconds".format(count, 
            (time.time() - _start)))

13
ตัวอย่างที่ดี เช่นเดียวกับข้อมูลเพิ่มเติมเพื่อจัดการกับความสับสนของ OP ... ตัวอย่างนี้แสดงให้เห็นว่าคิวที่ใช้ร่วมกันต้องมาจากกระบวนการหลักซึ่งจะถูกส่งต่อไปยังกระบวนการย่อยทั้งหมด เพื่อให้กระบวนการสองกระบวนการที่ไม่เกี่ยวข้องกันโดยสิ้นเชิงในการแชร์ข้อมูลพวกเขาต้องสื่อสารผ่านอุปกรณ์เครือข่ายส่วนกลางหรือที่เกี่ยวข้อง (เช่นซ็อกเก็ต) มีบางอย่างประสานข้อมูล
jdi

5
ตัวอย่างที่ดี .. ฉันยังใหม่กับหัวข้อนี้ .. ถ้าฉันมีหลายกระบวนการที่เรียกใช้ฟังก์ชันเป้าหมายเดียวกัน (ที่มีอาร์กิวเมนต์ต่างกัน) วิธีตรวจสอบให้แน่ใจว่าพวกเขาไม่ปะทะกันในขณะที่ใส่ข้อมูลลงในคิว .. จำเป็นต้องล็อก เหรอ?
WYSIWYG

@bharat_iyengar จากเอกสารประกอบโมดูลการประมวลผลหลายขั้นตอนระบุว่ามีการใช้คิวโดยใช้การล็อก / เซมาโฟร์เพียงไม่กี่ตัว ดังนั้นเมื่อคุณใช้เมธอด get () และ put (object) Queue คิวจะบล็อกหากกระบวนการ / เธรดอื่นกำลังพยายามรับหรือวางบางสิ่งในคิว คุณจึงไม่ต้องกังวลเกี่ยวกับการล็อกด้วยตนเอง
almel

1
เงื่อนไขการหยุดที่ชัดเจนดีกว่าเงื่อนไขการหยุดโดยปริยาย
Mike Pennington

2
Qsize สามารถไปที่ศูนย์ได้หากผู้อ่านคิวเกินอัตราของผู้เขียนคิว
Mike Pennington

8

ใน " from queue import Queue" มีโมดูลไม่มีเรียกqueueแทนmultiprocessingควรจะใช้ ดังนั้นควรมีลักษณะดังนี้ " from multiprocessing import Queue"


11
ในขณะที่ปลายปีการใช้multiprocessing.Queueถูกต้อง ปกติQueue.Queueจะใช้สำหรับการหลามหัวข้อ เมื่อคุณพยายามใช้Queue.Queueกับการประมวลผลหลายขั้นตอนสำเนาของออบเจ็กต์คิวจะถูกสร้างขึ้นในแต่ละกระบวนการย่อยและกระบวนการลูกจะไม่ถูกอัพเดต โดยทั่วไปQueue.Queueทำงานโดยใช้วัตถุที่แชร์ร่วมกันและmultiprocessing.Queueทำงานโดยใช้ IPC ดู: stackoverflow.com/questions/925100/…
Michael Guffre

5

นี่คือการใช้งานที่เรียบง่ายmultiprocessing.Queueและmultiprocessing.Processช่วยให้ผู้โทรสามารถส่ง "เหตุการณ์" พร้อมอาร์กิวเมนต์ไปยังกระบวนการแยกต่างหากที่ส่งเหตุการณ์ไปยังวิธีการ "do_" ในกระบวนการ (Python 3.4+)

import multiprocessing as mp
import collections

Msg = collections.namedtuple('Msg', ['event', 'args'])

class BaseProcess(mp.Process):
    """A process backed by an internal queue for simple one-way message passing.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.queue = mp.Queue()

    def send(self, event, *args):
        """Puts the event and args as a `Msg` on the queue
        """
       msg = Msg(event, args)
       self.queue.put(msg)

    def dispatch(self, msg):
        event, args = msg

        handler = getattr(self, "do_%s" % event, None)
        if not handler:
            raise NotImplementedError("Process has no handler for [%s]" % event)

        handler(*args)

    def run(self):
        while True:
            msg = self.queue.get()
            self.dispatch(msg)

การใช้งาน:

class MyProcess(BaseProcess):
    def do_helloworld(self, arg1, arg2):
        print(arg1, arg2)

if __name__ == "__main__":
    process = MyProcess()
    process.start()
    process.send('helloworld', 'hello', 'world')

sendที่เกิดขึ้นในการปกครองที่do_*เกิดขึ้นในกระบวนการย่อย

ฉันทิ้งการจัดการข้อยกเว้นใด ๆ ที่เห็นได้ชัดว่าจะขัดจังหวะการวนรอบการทำงานและออกจากกระบวนการย่อย คุณยังสามารถปรับแต่งได้โดยการลบล้างrunเพื่อควบคุมการบล็อกหรือสิ่งอื่นใด

สิ่งนี้มีประโยชน์จริงๆในสถานการณ์ที่คุณมีกระบวนการทำงานคนเดียว แต่ฉันคิดว่ามันเป็นคำตอบที่เกี่ยวข้องสำหรับคำถามนี้เพื่อแสดงให้เห็นสถานการณ์ทั่วไปที่มีการวางแนววัตถุมากขึ้นเล็กน้อย


1
คำตอบที่โดดเด่น! ขอบคุณ. +50 :)
kmiklas

3

ฉันได้ดูคำตอบหลายคำตอบในสแต็กโอเวอร์โฟลว์และเว็บในขณะที่พยายามตั้งค่าวิธีการประมวลผลหลายขั้นตอนโดยใช้คิวเพื่อส่งผ่านดาต้าเฟรมของแพนด้าขนาดใหญ่ สำหรับฉันแล้วดูเหมือนว่าทุกคำตอบกำลังทำซ้ำวิธีการแก้ปัญหาแบบเดิมโดยไม่ต้องคำนึงถึงกรณีขอบจำนวนมากที่เราจะเจอเมื่อตั้งค่าการคำนวณเช่นนี้ ปัญหาคือมีหลายอย่างในเวลาเดียวกัน จำนวนงานจำนวนคนงานระยะเวลาของแต่ละงานและข้อยกเว้นที่เป็นไปได้ระหว่างการดำเนินงาน สิ่งเหล่านี้ทำให้การซิงโครไนซ์ยุ่งยากและคำตอบส่วนใหญ่ไม่ได้อยู่ที่ว่าคุณจะทำอย่างไร นี่คือสิ่งที่ฉันใช้หลังจากเล่นซอไปสองสามชั่วโมงหวังว่านี่จะเป็นเรื่องทั่วไปเพียงพอสำหรับคนส่วนใหญ่ที่จะพบว่ามีประโยชน์

ความคิดบางอย่างก่อนตัวอย่างการเข้ารหัสใด ๆ เนื่องจากqueue.Emptyหรือqueue.qsize()หรือวิธีการอื่นที่คล้ายคลึงกันไม่น่าเชื่อถือสำหรับการควบคุมการไหลรหัสใด ๆ ที่คล้ายกัน

while True:
    try:
        task = pending_queue.get_nowait()
    except queue.Empty:
        break

เป็นของปลอม สิ่งนี้จะฆ่าคนงานแม้ว่ามิลลิวินาทีต่อมามีงานอื่นปรากฏขึ้นในคิว คนงานจะไม่ฟื้นตัวและหลังจากนั้นไม่นานคนงานทั้งหมดจะหายไปเมื่อพวกเขาสุ่มพบว่าคิวว่างเปล่าชั่วขณะ ผลลัพธ์สุดท้ายคือฟังก์ชันมัลติโพรเซสซิงหลัก (ฟังก์ชันที่มีการเข้าร่วม () บนโปรเซส) จะส่งคืนโดยที่งานทั้งหมดไม่เสร็จสมบูรณ์ ดี. ขอให้โชคดีในการแก้ไขข้อบกพร่องนั้นหากคุณมีงานหลายพันงานและขาดหายไป

ปัญหาอื่น ๆ คือการใช้ค่า Sentinel หลายคนแนะนำให้เพิ่มค่า sentinel ในคิวเพื่อตั้งค่าสถานะจุดสิ้นสุดของคิว แต่จะฟันธงว่าใครกันแน่? หากมีคนงาน N สมมติว่า N คือจำนวนคอร์ที่มีให้หรือรับค่า Sentinel เดียวจะตั้งค่าสถานะจุดสิ้นสุดของคิวให้กับผู้ปฏิบัติงานหนึ่งคนเท่านั้น คนงานคนอื่น ๆ ทั้งหมดจะนั่งรองานเพิ่มเมื่อไม่มีเหลือ ตัวอย่างทั่วไปที่ฉันเคยเห็นคือ

while True:
    task = pending_queue.get()
    if task == SOME_SENTINEL_VALUE:
        break

คนงานคนหนึ่งจะได้รับค่า Sentinel ในขณะที่คนที่เหลือจะรอไปเรื่อย ๆ ไม่มีโพสต์ที่ฉันพูดถึงว่าคุณต้องส่งค่า Sentinel ไปยังคิวอย่างน้อยหลาย ๆ ครั้งตามที่คุณมีคนงานเพื่อให้ทุกคนได้รับ

ปัญหาอื่น ๆ คือการจัดการข้อยกเว้นระหว่างการดำเนินงาน อีกครั้งสิ่งเหล่านี้ควรถูกจับและจัดการ ยิ่งไปกว่านั้นหากคุณมีไฟล์completed_tasksคิวคุณควรนับด้วยวิธีกำหนดอย่างอิสระว่ามีกี่รายการในคิวก่อนที่คุณจะตัดสินใจว่างานเสร็จแล้ว การใช้ขนาดคิวอีกครั้งจะทำให้ล้มเหลวและส่งกลับผลลัพธ์ที่ไม่คาดคิด

ในตัวอย่างด้านล่างpar_proc()ฟังก์ชันจะได้รับรายการงานรวมถึงฟังก์ชันที่ควรดำเนินการกับงานเหล่านี้ควบคู่ไปกับอาร์กิวเมนต์และค่าที่ระบุชื่อ

import multiprocessing as mp
import dill as pickle
import queue
import time
import psutil

SENTINEL = None


def do_work(tasks_pending, tasks_completed):
    # Get the current worker's name
    worker_name = mp.current_process().name

    while True:
        try:
            task = tasks_pending.get_nowait()
        except queue.Empty:
            print(worker_name + ' found an empty queue. Sleeping for a while before checking again...')
            time.sleep(0.01)
        else:
            try:
                if task == SENTINEL:
                    print(worker_name + ' no more work left to be done. Exiting...')
                    break

                print(worker_name + ' received some work... ')
                time_start = time.perf_counter()
                work_func = pickle.loads(task['func'])
                result = work_func(**task['task'])
                tasks_completed.put({work_func.__name__: result})
                time_end = time.perf_counter() - time_start
                print(worker_name + ' done in {} seconds'.format(round(time_end, 5)))
            except Exception as e:
                print(worker_name + ' task failed. ' + str(e))
                tasks_completed.put({work_func.__name__: None})


def par_proc(job_list, num_cpus=None):

    # Get the number of cores
    if not num_cpus:
        num_cpus = psutil.cpu_count(logical=False)

    print('* Parallel processing')
    print('* Running on {} cores'.format(num_cpus))

    # Set-up the queues for sending and receiving data to/from the workers
    tasks_pending = mp.Queue()
    tasks_completed = mp.Queue()

    # Gather processes and results here
    processes = []
    results = []

    # Count tasks
    num_tasks = 0

    # Add the tasks to the queue
    for job in job_list:
        for task in job['tasks']:
            expanded_job = {}
            num_tasks = num_tasks + 1
            expanded_job.update({'func': pickle.dumps(job['func'])})
            expanded_job.update({'task': task})
            tasks_pending.put(expanded_job)

    # Use as many workers as there are cores (usually chokes the system so better use less)
    num_workers = num_cpus

    # We need as many sentinels as there are worker processes so that ALL processes exit when there is no more
    # work left to be done.
    for c in range(num_workers):
        tasks_pending.put(SENTINEL)

    print('* Number of tasks: {}'.format(num_tasks))

    # Set-up and start the workers
    for c in range(num_workers):
        p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed))
        p.name = 'worker' + str(c)
        processes.append(p)
        p.start()

    # Gather the results
    completed_tasks_counter = 0
    while completed_tasks_counter < num_tasks:
        results.append(tasks_completed.get())
        completed_tasks_counter = completed_tasks_counter + 1

    for p in processes:
        p.join()

    return results

และนี่คือการทดสอบเพื่อเรียกใช้โค้ดด้านบนกับ

def test_parallel_processing():
    def heavy_duty1(arg1, arg2, arg3):
        return arg1 + arg2 + arg3

    def heavy_duty2(arg1, arg2, arg3):
        return arg1 * arg2 * arg3

    task_list = [
        {'func': heavy_duty1, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
        {'func': heavy_duty2, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
    ]

    results = par_proc(task_list)

    job1 = sum([y for x in results if 'heavy_duty1' in x.keys() for y in list(x.values())])
    job2 = sum([y for x in results if 'heavy_duty2' in x.keys() for y in list(x.values())])

    assert job1 == 15
    assert job2 == 21

บวกอีกอันโดยมีข้อยกเว้นบางประการ

def test_parallel_processing_exceptions():
    def heavy_duty1_raises(arg1, arg2, arg3):
        raise ValueError('Exception raised')
        return arg1 + arg2 + arg3

    def heavy_duty2(arg1, arg2, arg3):
        return arg1 * arg2 * arg3

    task_list = [
        {'func': heavy_duty1_raises, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
        {'func': heavy_duty2, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
    ]

    results = par_proc(task_list)

    job1 = sum([y for x in results if 'heavy_duty1' in x.keys() for y in list(x.values())])
    job2 = sum([y for x in results if 'heavy_duty2' in x.keys() for y in list(x.values())])

    assert not job1
    assert job2 == 21

หวังว่าจะเป็นประโยชน์


2

เราติดตั้งสองเวอร์ชันนี้หนึ่งเป็นเธรดพูลแบบหลายเธรดธรรมดาที่สามารถเรียกใช้งาน callables หลายประเภททำให้ชีวิตของเราง่ายขึ้นมากและเวอร์ชันที่สองที่ใช้กระบวนการซึ่งมีความยืดหยุ่นน้อยกว่าในแง่ของ callables และความต้องการและการโทรพิเศษเพื่อ dill

การตั้งค่า frozen_pool เป็น true จะหยุดการดำเนินการจนกว่าจะเรียก finish_pool_queue ในคลาสใดคลาสหนึ่ง

เวอร์ชันเธรด:

'''
Created on Nov 4, 2019

@author: Kevin
'''
from threading import Lock, Thread
from Queue import Queue
import traceback
from helium.loaders.loader_retailers import print_info
from time import sleep
import signal
import os

class ThreadPool(object):
    def __init__(self, queue_threads, *args, **kwargs):
        self.frozen_pool = kwargs.get('frozen_pool', False)
        self.print_queue = kwargs.get('print_queue', True)
        self.pool_results = []
        self.lock = Lock()
        self.queue_threads = queue_threads
        self.queue = Queue()
        self.threads = []

        for i in range(self.queue_threads):
            t = Thread(target=self.make_pool_call)
            t.daemon = True
            t.start()
            self.threads.append(t)

    def make_pool_call(self):
        while True:
            if self.frozen_pool:
                #print '--> Queue is frozen'
                sleep(1)
                continue

            item = self.queue.get()
            if item is None:
                break

            call = item.get('call', None)
            args = item.get('args', [])
            kwargs = item.get('kwargs', {})
            keep_results = item.get('keep_results', False)

            try:
                result = call(*args, **kwargs)

                if keep_results:
                    self.lock.acquire()
                    self.pool_results.append((item, result))
                    self.lock.release()

            except Exception as e:
                self.lock.acquire()
                print e
                traceback.print_exc()
                self.lock.release()
                os.kill(os.getpid(), signal.SIGUSR1)

            self.queue.task_done()

    def finish_pool_queue(self):
        self.frozen_pool = False

        while self.queue.unfinished_tasks > 0:
            if self.print_queue:
                print_info('--> Thread pool... %s' % self.queue.unfinished_tasks)
            sleep(5)

        self.queue.join()

        for i in range(self.queue_threads):
            self.queue.put(None)

        for t in self.threads:
            t.join()

        del self.threads[:]

    def get_pool_results(self):
        return self.pool_results

    def clear_pool_results(self):
        del self.pool_results[:]

เวอร์ชันกระบวนการ:

  '''
Created on Nov 4, 2019

@author: Kevin
'''
import traceback
from helium.loaders.loader_retailers import print_info
from time import sleep
import signal
import os
from multiprocessing import Queue, Process, Value, Array, JoinableQueue, Lock,\
    RawArray, Manager
from dill import dill
import ctypes
from helium.misc.utils import ignore_exception
from mem_top import mem_top
import gc

class ProcessPool(object):
    def __init__(self, queue_processes, *args, **kwargs):
        self.frozen_pool = Value(ctypes.c_bool, kwargs.get('frozen_pool', False))
        self.print_queue = kwargs.get('print_queue', True)
        self.manager = Manager()
        self.pool_results = self.manager.list()
        self.queue_processes = queue_processes
        self.queue = JoinableQueue()
        self.processes = []

        for i in range(self.queue_processes):
            p = Process(target=self.make_pool_call)
            p.start()
            self.processes.append(p)

        print 'Processes', self.queue_processes

    def make_pool_call(self):
        while True:
            if self.frozen_pool.value:
                sleep(1)
                continue

            item_pickled = self.queue.get()

            if item_pickled is None:
                #print '--> Ending'
                self.queue.task_done()
                break

            item = dill.loads(item_pickled)

            call = item.get('call', None)
            args = item.get('args', [])
            kwargs = item.get('kwargs', {})
            keep_results = item.get('keep_results', False)

            try:
                result = call(*args, **kwargs)

                if keep_results:
                    self.pool_results.append(dill.dumps((item, result)))
                else:
                    del call, args, kwargs, keep_results, item, result

            except Exception as e:
                print e
                traceback.print_exc()
                os.kill(os.getpid(), signal.SIGUSR1)

            self.queue.task_done()

    def finish_pool_queue(self, callable=None):
        self.frozen_pool.value = False

        while self.queue._unfinished_tasks.get_value() > 0:
            if self.print_queue:
                print_info('--> Process pool... %s' % (self.queue._unfinished_tasks.get_value()))

            if callable:
                callable()

            sleep(5)

        for i in range(self.queue_processes):
            self.queue.put(None)

        self.queue.join()
        self.queue.close()

        for p in self.processes:
            with ignore_exception: p.join(10)
            with ignore_exception: p.terminate()

        with ignore_exception: del self.processes[:]

    def get_pool_results(self):
        return self.pool_results

    def clear_pool_results(self):
        del self.pool_results[:]
def test(eg):
        print 'EG', eg

โทรด้วย:

tp = ThreadPool(queue_threads=2)
tp.queue.put({'call': test, 'args': [random.randint(0, 100)]})
tp.finish_pool_queue()

หรือ

pp = ProcessPool(queue_processes=2)
pp.queue.put(dill.dumps({'call': test, 'args': [random.randint(0, 100)]}))
pp.queue.put(dill.dumps({'call': test, 'args': [random.randint(0, 100)]}))
pp.finish_pool_queue()

0

เพิ่งสร้างตัวอย่างง่ายๆและทั่วไปสำหรับการสาธิตการส่งข้อความผ่านคิวระหว่าง 2 โปรแกรมแบบสแตนด์อโลน ไม่ได้ตอบคำถามของ OP โดยตรง แต่ควรมีความชัดเจนเพียงพอที่จะระบุแนวคิด

เซิร์ฟเวอร์:

multiprocessing-queue-manager-server.py

import asyncio
import concurrent.futures
import multiprocessing
import multiprocessing.managers
import queue
import sys
import threading
from typing import Any, AnyStr, Dict, Union


class QueueManager(multiprocessing.managers.BaseManager):

    def get_queue(self, ident: Union[AnyStr, int, type(None)] = None) -> multiprocessing.Queue:
        pass


def get_queue(ident: Union[AnyStr, int, type(None)] = None) -> multiprocessing.Queue:
    global q

    if not ident in q:
        q[ident] = multiprocessing.Queue()

    return q[ident]


q: Dict[Union[AnyStr, int, type(None)], multiprocessing.Queue] = dict()
delattr(QueueManager, 'get_queue')


def init_queue_manager_server():
    if not hasattr(QueueManager, 'get_queue'):
        QueueManager.register('get_queue', get_queue)


def serve(no: int, term_ev: threading.Event):
    manager: QueueManager
    with QueueManager(authkey=QueueManager.__name__.encode()) as manager:
        print(f"Server address {no}: {manager.address}")

        while not term_ev.is_set():
            try:
                item: Any = manager.get_queue().get(timeout=0.1)
                print(f"Client {no}: {item} from {manager.address}")
            except queue.Empty:
                continue


async def main(n: int):
    init_queue_manager_server()
    term_ev: threading.Event = threading.Event()
    executor: concurrent.futures.ThreadPoolExecutor = concurrent.futures.ThreadPoolExecutor()

    i: int
    for i in range(n):
        asyncio.ensure_future(asyncio.get_running_loop().run_in_executor(executor, serve, i, term_ev))

    # Gracefully shut down
    try:
        await asyncio.get_running_loop().create_future()
    except asyncio.CancelledError:
        term_ev.set()
        executor.shutdown()
        raise


if __name__ == '__main__':
    asyncio.run(main(int(sys.argv[1])))

ลูกค้า:

multiprocessing-queue-manager-client.py

import multiprocessing
import multiprocessing.managers
import os
import sys
from typing import AnyStr, Union


class QueueManager(multiprocessing.managers.BaseManager):

    def get_queue(self, ident: Union[AnyStr, int, type(None)] = None) -> multiprocessing.Queue:
        pass


delattr(QueueManager, 'get_queue')


def init_queue_manager_client():
    if not hasattr(QueueManager, 'get_queue'):
        QueueManager.register('get_queue')


def main():
    init_queue_manager_client()

    manager: QueueManager = QueueManager(sys.argv[1], authkey=QueueManager.__name__.encode())
    manager.connect()

    message = f"A message from {os.getpid()}"
    print(f"Message to send: {message}")
    manager.get_queue().put(message)


if __name__ == '__main__':
    main()

การใช้งาน

เซิร์ฟเวอร์:

$ python3 multiprocessing-queue-manager-server.py N

Nเป็นจำนวนเต็มระบุจำนวนเซิร์ฟเวอร์ที่ควรสร้าง คัดลอกหนึ่งใน<server-address-N>ผลลัพธ์โดยเซิร์ฟเวอร์และทำให้เป็นอาร์กิวเมนต์แรกของแต่ละmultiprocessing-queue-manager-client.pyรายการ

ลูกค้า:

python3 multiprocessing-queue-manager-client.py <server-address-1>

ผลลัพธ์

เซิร์ฟเวอร์:

Client 1: <item> from <server-address-1>

Gist: https://gist.github.com/89062d639e40110c61c2f88018a8b0e5


UPD : สร้างแพ็คเกจที่นี่

เซิร์ฟเวอร์:

import ipcq


with ipcq.QueueManagerServer(address=ipcq.Address.DEFAULT, authkey=ipcq.AuthKey.DEFAULT) as server:
    server.get_queue().get()

ลูกค้า:

import ipcq


client = ipcq.QueueManagerClient(address=ipcq.Address.DEFAULT, authkey=ipcq.AuthKey.DEFAULT)
client.get_queue().put('a message')

ป้อนคำอธิบายภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.