นากกายรับกล่องงานสามใบที่เสร็จไม่พร้อมกันบนสายพานมิ้นต์

Python as_completed: เก็บผลงานขนานทีละชิ้นตามลำดับเสร็จ

น้องกายEngineering

ยิงงานขนานแล้วต้องรอตัวช้าสุดทุกครั้งใช่ไหมครับ — ในไลบรารีมาตรฐานมี concurrent.futures.as_completed ที่คืน future ตามลำดับเสร็จ ไม่ใช่ลำดับส่ง

จุดที่ใช้บ่อย:

  • แสดงความคืบหน้า / เขียนล็อกทันทีที่งานใดงานหนึ่งจบ
  • ยกเลิกงานที่เหลือเมื่อเจอผลที่ “พอแล้ว” (เช่น ค้นหาเจอตัวแรกที่ผ่านเกณฑ์)
  • อย่าลืมเรียก future.result() ในลูป — ถ้างานล้ม exception จะพุ่งตอนนั้น
import unittest
from concurrent.futures import ThreadPoolExecutor, as_completed
import time


def work(n):
    time.sleep(0.01 * (3 - n))
    return n * 10


class AsCompletedTests(unittest.TestCase):
    def test_yields_in_completion_order(self):
        with ThreadPoolExecutor(max_workers=3) as pool:
            futures = [pool.submit(work, i) for i in (0, 1, 2)]
            done = [f.result() for f in as_completed(futures)]
        self.assertEqual(sorted(done), [0, 10, 20])
        self.assertEqual(done[0], 20)  # n=2 sleeps least


if __name__ == "__main__":
    unittest.main()

Recap

  • เปลี่ยนจาก executor.map เป็น as_completed เมื่องานเสร็จไม่พร้อมกันและอยากตอบสนองเร็ว
  • เรียก result() ในลูปและจับ exception ต่อ future
  • ตั้ง timeout ใน as_completed(...) ถ้าไม่อยากค้างทั้งชุด