Chia sẻ hàng đợi kết quả giữa một số quy trình


92

Tài liệu cho multiprocessingmô-đun chỉ ra cách chuyển hàng đợi đến một quy trình được bắt đầu bằng multiprocessing.Process. Nhưng làm cách nào để chia sẻ hàng đợi với các quy trình không đồng bộ của worker được bắt đầu apply_async? Tôi không cần tham gia động hoặc bất cứ điều gì khác, chỉ là một cách để người lao động (lặp đi lặp lại) báo cáo kết quả của họ về cơ sở.

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    q = multiprocessing.Queue()
    workers = pool.apply_async(worker, (33, q))

Đây không thành công với: RuntimeError: Queue objects should only be shared between processes through inheritance. Tôi hiểu điều này có nghĩa là gì và tôi hiểu lời khuyên nên kế thừa thay vì yêu cầu chọn / bỏ chọn (và tất cả các hạn chế đặc biệt của Windows). Nhưng làm cách nào để vượt qua hàng đợi một cách hiệu quả? Tôi không thể tìm thấy một ví dụ và tôi đã thử một số lựa chọn thay thế nhưng không thành công theo nhiều cách khác nhau. Giúp tôi với?

Câu trả lời:


133

Hãy thử sử dụng multiprocessing.Manager để quản lý hàng đợi của bạn và cũng để các nhân viên khác nhau có thể truy cập vào hàng đợi.

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    m = multiprocessing.Manager()
    q = m.Queue()
    workers = pool.apply_async(worker, (33, q))

Điều đó đã làm được, cảm ơn! Đã xảy ra sự cố không liên quan với cuộc gọi không đồng bộ trong mã gốc của tôi, vì vậy tôi cũng đã sao chép bản sửa lỗi vào câu trả lời của bạn.
alexis

16
Bất kỳ lời giải thích tại sao queue.Queue()không phù hợp cho điều này?
mrgloom

@mrgloom: queue.Queueđược xây dựng để phân luồng, sử dụng khóa trong bộ nhớ. Trong môi trường Đa quy trình, mỗi quy trình con sẽ nhận được bản sao của một queue.Queue()cá thể riêng trong không gian bộ nhớ của riêng chúng, vì các quy trình con không chia sẻ bộ nhớ (chủ yếu).
LeoRochael

@alexis Làm cách nào để lấy các phần tử từ Manager (). Queue () sau khi nhiều nhân viên đã chèn dữ liệu vào đó?
MSS

10

multiprocessing.Poolđã có một hàng đợi kết quả được chia sẻ, không cần thêm vào a Manager.Queue. Manager.Queuelà một queue.Queue(hàng đợi đa luồng) nằm dưới mui xe, nằm trên một quy trình máy chủ riêng biệt và được hiển thị qua proxy. Điều này bổ sung thêm chi phí so với hàng đợi nội bộ của Pool. Trái ngược với việc dựa vào xử lý kết quả gốc của Pool, các kết quả trong Manager.Queuecũng không được đảm bảo theo thứ tự.

Các quy trình công nhân không được bắt đầu .apply_async(), điều này đã xảy ra khi bạn khởi tạo Pool. Những gì được bắt đầu khi bạn gọi pool.apply_async()là một "công việc" mới. Các quy trình công nhân của Pool vận hành multiprocessing.pool.workerchức năng dưới mui xe. Chức năng này đảm nhận việc xử lý các "nhiệm vụ" mới được chuyển qua nội bộ của Pool Pool._inqueuevà gửi kết quả trở lại phụ huynh qua Pool._outqueue. Chỉ định của bạn funcsẽ được thực hiện trong multiprocessing.pool.worker. funcchỉ có returnmột cái gì đó và kết quả sẽ được tự động gửi lại cho phụ huynh.

.apply_async() ngay lập tức (không đồng bộ) trả về một AsyncResultđối tượng (bí danh cho ApplyResult). Bạn cần gọi .get()(đang chặn) trên đối tượng đó để nhận được kết quả thực tế. Một tùy chọn khác là đăng ký một hàm gọi lại , hàm này sẽ được kích hoạt ngay khi kết quả sẵn sàng.

from multiprocessing import Pool

def busy_foo(i):
    """Dummy function simulating cpu-bound work."""
    for _ in range(int(10e6)):  # do stuff
        pass
    return i

if __name__ == '__main__':

    with Pool(4) as pool:
        print(pool._outqueue)  # DEMO
        results = [pool.apply_async(busy_foo, (i,)) for i in range(10)]
        # `.apply_async()` immediately returns AsyncResult (ApplyResult) object
        print(results[0])  # DEMO
        results = [res.get() for res in results]
        print(f'result: {results}')       

Đầu ra mẫu:

<multiprocessing.queues.SimpleQueue object at 0x7fa124fd67f0>
<multiprocessing.pool.ApplyResult object at 0x7fa12586da20>
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Lưu ý: Việc chỉ định timeout-parameter for .get()sẽ không dừng quá trình xử lý thực tế của tác vụ trong worker, nó chỉ bỏ chặn cha mẹ đang chờ bằng cách tăng a multiprocessing.TimeoutError.


Thật thú vị, tôi sẽ thử ngay khi có cơ hội đầu tiên. Nó chắc chắn đã không hoạt động theo cách này vào năm 2012.
alexis

@alexis Python 2.7 (2010) có liên quan ở đây chỉ thiếu trình quản lý ngữ cảnh và error_callback-parameter cho apply_async, vì vậy nó không thay đổi nhiều kể từ đó.
Darkonaut
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.