การแบ่งปันคิวผลลัพธ์ระหว่างกระบวนการต่างๆ


96

เอกสารสำหรับการแสดงโมดูลวิธีการส่งคิวให้กระบวนการที่เริ่มต้นด้วยmultiprocessing multiprocessing.Processแต่ฉันจะแชร์คิวกับกระบวนการของผู้ปฏิบัติงานแบบอะซิงโครนัสที่เริ่มต้นด้วยได้apply_asyncอย่างไร ฉันไม่ต้องการการเข้าร่วมแบบไดนามิกหรือสิ่งอื่นใดเป็นเพียงวิธีให้คนงาน (ซ้ำ ๆ ) รายงานผลกลับไปที่ฐาน

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

สิ่งนี้ล้มเหลวด้วย: RuntimeError: Queue objects should only be shared between processes through inheritance. ฉันเข้าใจว่าสิ่งนี้หมายถึงอะไรและฉันเข้าใจคำแนะนำในการสืบทอดแทนที่จะต้องการการดอง / การไม่หยิบ (และข้อ จำกัด พิเศษทั้งหมดของ Windows) แต่ฉันจะผ่านคิวด้วยวิธีใดได้ผล? ฉันหาตัวอย่างไม่ได้และฉันได้ลองใช้ทางเลือกต่างๆที่ล้มเหลวหลายวิธี ช่วยด้วย?

คำตอบ:


138

ลองใช้การประมวลผลหลายขั้นตอนผู้จัดการเพื่อจัดการคิวของคุณและเพื่อให้ผู้ปฏิบัติงานต่างๆสามารถเข้าถึงได้

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

ที่ทำได้ขอบคุณ! มีปัญหาที่ไม่เกี่ยวข้องกับการโทรแบบ async ในรหัสเดิมของฉันฉันจึงคัดลอกการแก้ไขไปยังคำตอบของคุณด้วย
alexis

17
คำอธิบายใด ๆ ที่queue.Queue()ไม่เหมาะสำหรับสิ่งนี้?
mrgloom

1
@mrgloom: queue.Queueสร้างขึ้นสำหรับเธรดโดยใช้การล็อกในหน่วยความจำ ในสภาพแวดล้อมแบบหลายกระบวนการแต่ละกระบวนการย่อยจะได้รับสำเนาqueue.Queue()อินสแตนซ์ของตัวเองในพื้นที่หน่วยความจำของตนเองเนื่องจากกระบวนการย่อยจะไม่แชร์หน่วยความจำ (ส่วนใหญ่)
LeoRochael

1
@alexis วิธีรับองค์ประกอบจาก Manager (). Queue () หลังจากคนงานหลายคนแทรกข้อมูลลงในนั้น?
MSS


14

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

กระบวนการปฏิบัติงานจะไม่ได้เริ่มต้นด้วยการนี้แล้วเกิดขึ้นเมื่อคุณยกตัวอย่าง.apply_async() Poolสิ่งที่จะเริ่มต้นเมื่อคุณเรียกpool.apply_async()เป็น "งาน" ใหม่ กระบวนการทำงานของ Pool เรียกใช้multiprocessing.pool.workerฟังก์ชันภายใต้ประทุน ฟังก์ชันนี้จะดูแลการประมวลผล "งาน" ใหม่ที่ถ่ายโอนผ่านภายในพูลPool._inqueueและการส่งผลลัพธ์กลับไปยังพาเรนPool._outqueueต์ ระบุของคุณจะได้รับการดำเนินการภายในfunc มีเพียงบางอย่างเท่านั้นและผลลัพธ์จะถูกส่งกลับไปยังผู้ปกครองโดยอัตโนมัติmultiprocessing.pool.workerfuncreturn

.apply_async() ทันที (แบบอะซิงโครนัส) ส่งคืนAsyncResultวัตถุ (นามแฝงสำหรับApplyResult) คุณต้องโทร.get()(กำลังบล็อก) บนวัตถุนั้นเพื่อรับผลลัพธ์ที่แท้จริง อีกทางเลือกหนึ่งคือการลงทะเบียนฟังก์ชันการโทรกลับซึ่งจะเริ่มทำงานทันทีที่ผลลัพธ์พร้อม

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

ตัวอย่างผลลัพธ์:

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

หมายเหตุ: การระบุtimeout-parameter สำหรับ.get()จะไม่หยุดการประมวลผลจริงของงานภายในผู้ปฏิบัติงาน แต่จะปลดบล็อกพาเรนต์ที่รออยู่โดยการเพิ่ม a multiprocessing.TimeoutError.


น่าสนใจฉันจะลองโอกาสแรกที่ได้รับ แน่นอนว่ามันไม่ได้ผลในปี 2012
alexis

@alexis Python 2.7 (2010) ที่เกี่ยวข้องที่นี่ขาดเพียงตัวจัดการบริบทและerror_callbackพารามิเตอร์สำหรับapply_asyncดังนั้นจึงไม่เปลี่ยนแปลงมากนักตั้งแต่นั้นมา
Darkonaut

ฉันพบว่าฟังก์ชันโทรกลับมีประโยชน์มากที่สุดโดยเฉพาะเมื่อรวมกับฟังก์ชันบางส่วนเพื่ออนุญาตให้ใช้รายการปกติเพื่อรวบรวมผลลัพธ์แบบ async ตามที่อธิบายไว้ที่นี่ gist.github.com/Glench/5789879
user5359531
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.