Ví dụ đơn giản về việc sử dụng Hàng đợi đa xử lý, Nhóm và Khóa


91

Tôi đã cố gắng đọc tài liệu tại http://docs.python.org/dev/library/multiprocessing.html nhưng tôi vẫn gặp khó khăn với Hàng đợi, Nhóm và Khóa đa xử lý. Và bây giờ tôi đã có thể xây dựng ví dụ bên dưới.

Về Hàng đợi và Nhóm, tôi không chắc mình đã hiểu khái niệm này theo cách đúng hay chưa, vì vậy hãy sửa cho tôi nếu tôi sai. Những gì tôi đang cố gắng đạt được là xử lý 2 yêu cầu cùng lúc (danh sách dữ liệu có 8 trong ví dụ này), vậy tôi nên sử dụng cái gì? Nhóm để tạo 2 quy trình có thể xử lý hai hàng đợi khác nhau (tối đa 2 hàng đợi) hay tôi chỉ nên sử dụng Hàng đợi để xử lý 2 đầu vào mỗi lần? Khóa sẽ in kết quả đầu ra một cách chính xác.

import multiprocessing
import time

data = (['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
        ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)


def mp_handler(var1):
    for indata in var1:
        p = multiprocessing.Process(target=mp_worker, args=(indata[0], indata[1]))
        p.start()


def mp_worker(inputs, the_time):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

if __name__ == '__main__':
    mp_handler(data)

Câu trả lời:


129

Giải pháp tốt nhất cho vấn đề của bạn là sử dụng a Pool. Sử dụng Queues và có một chức năng "cấp hàng đợi" riêng biệt có thể là quá mức cần thiết.

Đây là phiên bản được sắp xếp lại một chút của chương trình của bạn, lần này chỉ có 2 quy trình được tập hợp trong a Pool. Tôi tin rằng đó là cách dễ dàng nhất để thực hiện, với những thay đổi tối thiểu đối với mã gốc:

import multiprocessing
import time

data = (
    ['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
    ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)

def mp_worker((inputs, the_time)):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

def mp_handler():
    p = multiprocessing.Pool(2)
    p.map(mp_worker, data)

if __name__ == '__main__':
    mp_handler()

Lưu ý rằng mp_worker()hàm giờ đây chấp nhận một đối số (một bộ trong số hai đối số trước đó) vì map()hàm phân chia dữ liệu đầu vào của bạn thành các danh sách con, mỗi danh sách con được cung cấp như một đối số duy nhất cho hàm worker của bạn.

Đầu ra:

Processs a  Waiting 2 seconds
Processs b  Waiting 4 seconds
Process a   DONE
Processs c  Waiting 6 seconds
Process b   DONE
Processs d  Waiting 8 seconds
Process c   DONE
Processs e  Waiting 1 seconds
Process e   DONE
Processs f  Waiting 3 seconds
Process d   DONE
Processs g  Waiting 5 seconds
Process f   DONE
Processs h  Waiting 7 seconds
Process g   DONE
Process h   DONE

Chỉnh sửa theo nhận xét @Thales bên dưới:

Nếu bạn muốn "khóa cho từng giới hạn nhóm" để các quy trình của bạn chạy song song với nhau, hãy ala:

A chờ đợi B chờ đợi | A xong, B xong | C chờ đợi, D chờ đợi | C xong, D xong | ...

sau đó thay đổi hàm xử lý để khởi chạy các nhóm (của 2 quy trình) cho mỗi cặp dữ liệu:

def mp_handler():
    subdata = zip(data[0::2], data[1::2])
    for task1, task2 in subdata:
        p = multiprocessing.Pool(2)
        p.map(mp_worker, (task1, task2))

Bây giờ đầu ra của bạn là:

 Processs a Waiting 2 seconds
 Processs b Waiting 4 seconds
 Process a  DONE
 Process b  DONE
 Processs c Waiting 6 seconds
 Processs d Waiting 8 seconds
 Process c  DONE
 Process d  DONE
 Processs e Waiting 1 seconds
 Processs f Waiting 3 seconds
 Process e  DONE
 Process f  DONE
 Processs g Waiting 5 seconds
 Processs h Waiting 7 seconds
 Process g  DONE
 Process h  DONE

Cảm ơn vì ví dụ đơn giản và trực tiếp về cách thực hiện, Nhưng làm thế nào tôi có thể áp dụng khóa cho từng giới hạn nhóm? Ý tôi là, nếu bạn thực thi mã, tôi muốn nhìn thấy một cái gì đó giống như "Một đợi chờ B | A xong, b làm | C chờ đợi, chờ đợi D | C thực hiện, D thực hiện"
thclpr

2
Nói cách khác, bạn không muốn C bắt đầu cho đến khi cả A và B hoàn thành?
Velimir Mlaker

Chính xác, tôi có thể làm điều đó bằng multiprocessing.Process nhưng tôi không thể tìm ra cách để làm điều đó bằng hồ bơi
thclpr

Cảm ơn bạn rất nhiều, làm việc như dự định, nhưng về chức năng mp_handler bạn đang tham khảo các dữ liệu biến thay vì var1 :)
thclpr

Được rồi, cảm ơn, tôi đã xóa var1hoàn toàn, datathay vào đó là toàn cầu .
Velimir Mlaker

8

Điều này có thể không liên quan 100% đến câu hỏi, nhưng trên tìm kiếm của tôi về một ví dụ về sử dụng đa xử lý với một hàng đợi, điều này hiển thị đầu tiên trên google.

Đây là một lớp ví dụ cơ bản mà bạn có thể khởi tạo và đặt các mục vào hàng đợi và có thể đợi cho đến khi hàng đợi kết thúc. Đó là tất cả những gì tôi cần.

from multiprocessing import JoinableQueue
from multiprocessing.context import Process


class Renderer:
    queue = None

    def __init__(self, nb_workers=2):
        self.queue = JoinableQueue()
        self.processes = [Process(target=self.upload) for i in range(nb_workers)]
        for p in self.processes:
            p.start()

    def render(self, item):
        self.queue.put(item)

    def upload(self):
        while True:
            item = self.queue.get()
            if item is None:
                break

            # process your item here

            self.queue.task_done()

    def terminate(self):
        """ wait until queue is empty and terminate processes """
        self.queue.join()
        for p in self.processes:
            p.terminate()

r = Renderer()
r.render(item1)
r.render(item2)
r.terminate()

2
Là gì item1và là item2gì? Chúng có phải là một số loại nhiệm vụ hoặc chức năng, sẽ được thực thi trong hai quy trình khác nhau không?
Zelphir Kaltstahl

2
có, chúng là các tác vụ hoặc tham số đầu vào được xử lý theo cách song song.
linqu

8

Đây là goto cá nhân của tôi cho chủ đề này:

Gist tại đây, (chào mừng các yêu cầu kéo!): Https://gist.github.com/thorsummoner/b5b1dfcff7e7fdd334ec

import multiprocessing
import sys

THREADS = 3

# Used to prevent multiple threads from mixing thier output
GLOBALLOCK = multiprocessing.Lock()


def func_worker(args):
    """This function will be called by each thread.
    This function can not be a class method.
    """
    # Expand list of args into named args.
    str1, str2 = args
    del args

    # Work
    # ...



    # Serial-only Portion
    GLOBALLOCK.acquire()
    print(str1)
    print(str2)
    GLOBALLOCK.release()


def main(argp=None):
    """Multiprocessing Spawn Example
    """
    # Create the number of threads you want
    pool = multiprocessing.Pool(THREADS)

    # Define two jobs, each with two args.
    func_args = [
        ('Hello', 'World',), 
        ('Goodbye', 'World',), 
    ]


    try:
        # Spawn up to 9999999 jobs, I think this is the maximum possible.
        # I do not know what happens if you exceed this.
        pool.map_async(func_worker, func_args).get(9999999)
    except KeyboardInterrupt:
        # Allow ^C to interrupt from any thread.
        sys.stdout.write('\033[0m')
        sys.stdout.write('User Interupt\n')
    pool.close()

if __name__ == '__main__':
    main()

1
Tôi không chắc chính xác là .map_async () có tốt hơn .map () theo bất kỳ cách nào không.
ThorSummoner

3
Đối số get()là thời gian chờ, không liên quan gì đến số lượng công việc được bắt đầu.
mata

@mata vậy, điều đó có nghĩa là được sử dụng trong vòng lặp bỏ phiếu không? .get(timeout=1)? và có ổn không nếu chỉ nói .get()để lấy danh sách đã hoàn thành?
ThorSummoner

Có, .get()chờ vô thời hạn cho đến khi có tất cả kết quả và trả về danh sách kết quả. Bạn có thể sử dụng vòng lặp thăm dò để kiểm tra kết quả thời tiết có sẵn hoặc bạn có thể chuyển một hàm gọi lại trong lệnh map_async()gọi, hàm này sau đó sẽ được gọi cho mọi kết quả khi nó có sẵn.
mata

2

Đối với mọi người sử dụng các trình chỉnh sửa như Komodo Edit (win10), hãy thêm sys.stdout.flush()vào:

def mp_worker((inputs, the_time)):
    print " Process %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs
    sys.stdout.flush()

hoặc như dòng đầu tiên đến:

    if __name__ == '__main__':
       sys.stdout.flush()

Điều này giúp xem những gì diễn ra trong quá trình chạy script; thay vì phải nhìn vào hộp dòng lệnh màu đen.


1

Đây là một ví dụ từ mã của tôi (cho nhóm phân luồng, nhưng chỉ cần thay đổi tên lớp và bạn sẽ có nhóm xử lý):

def execute_run(rp): 
   ... do something 

pool = ThreadPoolExecutor(6)
for mat in TESTED_MATERIAL:
    for en in TESTED_ENERGIES:
        for ecut in TESTED_E_CUT:
            rp = RunParams(
                simulations, DEST_DIR,
                PARTICLE, mat, 960, 0.125, ecut, en
            )
            pool.submit(execute_run, rp)
pool.join()

Về cơ bản:

  • pool = ThreadPoolExecutor(6) tạo một nhóm cho 6 chủ đề
  • Sau đó, bạn có một loạt các for có thể thêm các nhiệm vụ vào nhóm
  • pool.submit(execute_run, rp) thêm một tác vụ vào nhóm, đối số đầu tiên là một hàm được gọi trong một luồng / tiến trình, phần còn lại của các đối số được chuyển cho hàm được gọi.
  • pool.join đợi cho đến khi tất cả các nhiệm vụ được thực hiện.

2
Lưu ý rằng bạn đang sử dụng concurrent.futures, nhưng OP đang hỏi về multiprocessingvà Python 2.7.
Tim Peters
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.