Làm thế nào để sử dụng hàng đợi đa xử lý trong Python?


94

Tôi đang gặp nhiều khó khăn khi cố gắng hiểu cách hàng đợi đa xử lý hoạt động trên python và cách triển khai nó. Giả sử tôi có hai mô-đun python truy cập dữ liệu từ một tệp được chia sẻ, hãy gọi hai mô-đun này là người viết và người đọc. Kế hoạch của tôi là yêu cầu cả người đọc và người viết đặt các yêu cầu vào hai hàng đợi đa xử lý riêng biệt, sau đó có quy trình thứ ba đưa các yêu cầu này vào một vòng lặp và thực thi như vậy.

Vấn đề chính của tôi là tôi thực sự không biết cách triển khai multiprocessing.queue một cách chính xác, bạn không thể thực sự khởi tạo đối tượng cho mỗi quy trình vì chúng sẽ là các hàng đợi riêng biệt, làm thế nào để bạn đảm bảo rằng tất cả các quy trình liên quan đến một hàng đợi được chia sẻ (hoặc trong trường hợp này, hàng đợi)


4
chuyển Hàng đợi cho mỗi lớp quy trình dưới dạng tham số khi bạn khởi tạo chúng trong quy trình mẹ.
Joel Cornett

Câu trả lời:


122

Vấn đề chính của tôi là tôi thực sự không biết cách triển khai multiprocessing.queue một cách chính xác, bạn không thể thực sự khởi tạo đối tượng cho mỗi quy trình vì chúng sẽ là các hàng đợi riêng biệt, làm thế nào để bạn đảm bảo rằng tất cả các quy trình liên quan đến một hàng đợi được chia sẻ (hoặc trong trường hợp này, hàng đợi)

Đây là một ví dụ đơn giản về việc một người đọc và người viết chia sẻ một hàng đợi duy nhất ... Người viết gửi một loạt các số nguyên cho người đọc; khi người viết sử dụng hết số, nó sẽ gửi 'DONE', điều này cho phép người đọc biết để thoát ra khỏi vòng lặp đọc.

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)))

12
Ví dụ tuyệt vời. Chỉ là một chút thông tin bổ sung để giải quyết sự nhầm lẫn của OP ... Ví dụ này cho thấy rằng một hàng đợi được chia sẻ cần bắt nguồn từ quy trình chính, sau đó được chuyển đến tất cả các quy trình con của nó. Để hai quy trình hoàn toàn không liên quan có thể chia sẻ dữ liệu, chúng phải giao tiếp qua một số thiết bị mạng trung tâm hoặc liên kết (ví dụ: ổ cắm). Một cái gì đó phải điều phối thông tin.
jdi 17-07-12

5
ví dụ hay .. tôi cũng là người mới tham gia chủ đề này .. nếu tôi có nhiều quy trình đang chạy cùng một hàm mục tiêu (với các đối số khác nhau), làm thế nào để đảm bảo rằng chúng không xung đột trong khi đưa dữ liệu vào hàng đợi .. là cần khóa ?
WYSIWYG

@bharat_iyengar Từ tài liệu mô-đun đa xử lý, nó nói rằng Hàng đợi được triển khai bằng cách sử dụng một vài khóa / semaphores. Vì vậy, khi bạn sử dụng các phương thức Hàng đợi get () và put (đối tượng), hàng đợi sẽ chặn nếu một số tiến trình / luồng khác đang cố gắng lấy hoặc đưa thứ gì đó vào hàng đợi. Vì vậy, bạn không phải lo lắng về việc khóa nó theo cách thủ công.
almel

1
Điều kiện dừng rõ ràng là tốt hơn so với điều kiện dừng ngầm
Mike Pennington

2
Qsize có thể đi đến số không nếu độc giả xếp hàng vượt quá mức của các nhà văn hàng đợi
Mike Pennington

7

trong " from queue import Queue" không có mô-đun nào được gọi queue, thay vào đó multiprocessingnên được sử dụng. Do đó, nó sẽ giống như " from multiprocessing import Queue"


10
Trong khi chậm năm, sử dụng multiprocessing.Queuelà chính xác. Bình thường Queue.Queueđược sử dụng cho các chủ đề python . Khi bạn cố gắng sử dụng Queue.Queuevới đa xử lý, các bản sao của đối tượng Hàng đợi sẽ được tạo trong mỗi quy trình con và các quy trình con sẽ không bao giờ được cập nhật. Về cơ bản, Queue.Queuehoạt động bằng cách sử dụng đối tượng được chia sẻ toàn cầu và multiprocessing.Queuehoạt động bằng IPC. Xem: stackoverflow.com/questions/925100/…
Michael Guffre

5

Đây là một cách sử dụng đơn giản đã chết multiprocessing.Queuemultiprocessing.Processcho phép người gọi gửi một "sự kiện" cùng với các đối số đến một quy trình riêng biệt gửi sự kiện đến một phương thức "do_" trong quy trình. (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)

Sử dụng:

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

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

Điều sendxảy ra trong quy trình mẹ,do_* xảy ra trong tiến trình con.

Tôi đã bỏ qua bất kỳ xử lý ngoại lệ nào rõ ràng sẽ làm gián đoạn vòng lặp chạy và thoát khỏi quy trình con. Bạn cũng có thể tùy chỉnh nó bằng cách ghi đè runđể kiểm soát chặn hoặc bất cứ điều gì khác.

Điều này thực sự chỉ hữu ích trong các tình huống mà bạn có một quy trình riêng lẻ, nhưng tôi nghĩ rằng đó là một câu trả lời phù hợp cho câu hỏi này để chứng minh một kịch bản chung với hướng đối tượng hơn một chút.


1
Câu trả lời xuất sắc! Cảm ơn bạn. +50 :)
kmiklas

3

Tôi đã xem xét nhiều câu trả lời trên tràn ngăn xếp và web trong khi cố gắng thiết lập một cách thực hiện đa xử lý bằng cách sử dụng hàng đợi để chuyển xung quanh các khung dữ liệu gấu trúc lớn. Đối với tôi, dường như mọi câu trả lời đều lặp lại cùng một loại giải pháp mà không cần xem xét đến vô số trường hợp cạnh mà người ta chắc chắn sẽ gặp phải khi thiết lập các phép tính như thế này. Vấn đề là có nhiều thứ diễn ra cùng một lúc. Số lượng nhiệm vụ, số lượng công nhân, thời lượng của mỗi nhiệm vụ và các ngoại lệ có thể xảy ra trong quá trình thực thi nhiệm vụ. Tất cả những điều này làm cho việc đồng bộ hóa trở nên phức tạp và hầu hết các câu trả lời không giải quyết được cách bạn có thể thực hiện nó. Vì vậy, đây là kinh nghiệm của tôi sau khi mày mò trong vài giờ, hy vọng điều này sẽ đủ chung để hầu hết mọi người thấy nó hữu ích.

Một số suy nghĩ trước bất kỳ ví dụ mã hóa nào. Vì queue.Emptyhoặc queue.qsize()hoặc bất kỳ phương pháp tương tự nào khác không đáng tin cậy để kiểm soát luồng, bất kỳ mã nào tương tự

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

là không có thật. Điều này sẽ giết nhân viên ngay cả khi mili giây sau đó một nhiệm vụ khác xuất hiện trong hàng đợi. Công nhân sẽ không phục hồi và sau một lúc TẤT CẢ công nhân sẽ biến mất khi họ ngẫu nhiên tìm thấy hàng đợi trống trong giây lát. Kết quả cuối cùng sẽ là hàm đa xử lý chính (hàm có phép nối () trên các quy trình) sẽ trả về mà không cần hoàn thành tất cả các tác vụ. Đẹp. Chúc may mắn gỡ lỗi thông qua đó nếu bạn có hàng nghìn nhiệm vụ và một vài nhiệm vụ bị thiếu.

Vấn đề khác là việc sử dụng các giá trị sentinel. Nhiều người đã đề xuất thêm một giá trị sentinel trong hàng đợi để gắn cờ kết thúc hàng đợi. Nhưng để gắn cờ chính xác cho ai? Nếu có N công nhân, giả sử N là số lõi có sẵn cho hoặc nhận, thì một giá trị sentinel duy nhất sẽ chỉ gắn cờ kết thúc hàng đợi cho một công nhân. Tất cả các công nhân khác sẽ ngồi chờ thêm việc khi không còn việc nào. Ví dụ điển hình mà tôi đã thấy là

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

Một công nhân sẽ nhận được giá trị sentinel trong khi những người còn lại sẽ đợi vô thời hạn. Không có bài đăng nào tôi xem được đề cập rằng bạn cần phải gửi giá trị sentinel vào hàng đợi ÍT NHẤT bao nhiêu lần khi bạn có nhân viên để TẤT CẢ họ nhận được nó.

Vấn đề khác là việc xử lý các ngoại lệ trong quá trình thực thi tác vụ. Một lần nữa những điều này nên được bắt và quản lý. Hơn nữa, nếu bạn cócompleted_tasks hàng đợi, bạn nên đếm một cách độc lập theo cách xác định có bao nhiêu mục trong hàng đợi trước khi bạn quyết định rằng công việc đã hoàn thành. Một lần nữa dựa vào kích thước hàng đợi chắc chắn sẽ thất bại và trả về kết quả không mong muốn.

Trong ví dụ bên dưới, par_proc()hàm sẽ nhận một danh sách các tác vụ bao gồm các hàm mà các tác vụ này sẽ được thực thi cùng với bất kỳ đối số và giá trị nào được đặt tên.

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

Và đây là một bài kiểm tra để chạy đoạn mã trên với

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

cộng với một cái khác với một số ngoại lệ

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

Hy vọng điều đó là hữu ích.


2

Chúng tôi đã triển khai hai phiên bản của điều này, một là một nhóm đa luồng đơn giản có thể thực thi nhiều loại có thể gọi, giúp cuộc sống của chúng tôi dễ dàng hơn nhiều và phiên bản thứ hai sử dụng các quy trình , ít linh hoạt hơn về khả năng gọi và yêu cầu và thêm lệnh gọi để dill.

Việc đặt freeze_pool thành true sẽ đóng băng thực thi cho đến khi finish_pool_queue được gọi trong một trong hai lớp.

Phiên bản chủ đề:

'''
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[:]

Phiên bản Quy trình:

  '''
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

Gọi bằng một trong hai:

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

hoặc là

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

Vừa tạo một ví dụ đơn giản và tổng quát để chứng minh việc truyền thông điệp qua Hàng đợi giữa 2 chương trình độc lập. Nó không trực tiếp trả lời câu hỏi của OP nhưng phải đủ rõ ràng để chỉ ra khái niệm.

Người phục vụ:

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

Khách hàng:

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()

Sử dụng

Người phục vụ:

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

Nlà một số nguyên cho biết có bao nhiêu máy chủ nên được tạo. Sao chép một trong các kết <server-address-N>quả đầu ra của máy chủ và đặt nó làm đối số đầu tiên của mỗi kết quả multiprocessing-queue-manager-client.py.

Khách hàng:

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

Kết quả

Người phục vụ:

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

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


UPD : Đã tạo một gói ở đây .

Người phục vụ:

import ipcq


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

Khách hàng:

import ipcq


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

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.