8.4. Gather

บทก่อนหน้าแสดงให้เห็นว่า asyncio.create_task() จัดตารางคอรูทีนและ ไม่ รอมัน การดำเนินการคู่ ซึ่งก็คือ รันหลาย awaitable พร้อมกันและรอทั้งหมด คือ asyncio.gather()

8.4.1. รูปแบบพื้นฐาน

import asyncio

async def fetch(name, delay_ms):
    await asyncio.sleep_ms(delay_ms)
    return name

async def main():
    results = await asyncio.gather(
        fetch("a", 100),
        fetch("b", 200),
        fetch("c", 50),
    )
    print(results)

asyncio.run(main())

ผลลัพธ์:

['a', 'b', 'c']

สองสิ่งที่ควรสังเกต ประการแรก รายการผลลัพธ์อยู่ตามลำดับ อาร์กิวเมนต์ของ gather ไม่ใช่ลำดับที่คอรูทีนเสร็จสิ้น "c" คืนค่าก่อนแต่ยังคงเป็นรายการที่สาม ประการที่สอง การเรียกใช้เวลารวม 200 ms ไม่ใช่ 350 ms: การนอนหลับสามครั้งทำงานพร้อมกัน และ gather() คืนค่าทันทีที่ช้าที่สุดเสร็จสิ้น

ทั้งสองข้อเท็จจริงมาจากแหล่งเดียวกัน gather() ครอบแต่ละอาร์กิวเมนต์ที่ยังไม่เป็นงาน (คอรูทีนในตัวอย่าง) จัดตารางบน loop ระงับคอรูทีนที่เรียกจนกว่า ทั้งหมด เสร็จสิ้น แล้วคืนผลลัพธ์ตามลำดับเดิม

8.4.2. เมื่อใดควรใช้

ทุกที่ที่แอปพลิเคชันมีการดำเนินการ awaitable N ครั้งและต้องการผลลัพธ์ทั้งหมดก่อนดำเนินการต่อ ตัวอย่างทั่วไป:

  • ส่งคำขอเครือข่ายหลายรายการพร้อมกันและรอการตอบกลับทั้งหมด

  • อ่านจากเซนเซอร์หลายตัวพร้อมกันก่อนประมวลผลผลลัพธ์รวม

  • รวมงานตัวช่วยที่อายุสั้นหลายชิ้นเมื่อสิ้นสุดขั้นตอนของแอปพลิเคชัน

มัน ไม่ใช่ เครื่องมือที่เหมาะสมสำหรับงานพื้นหลังที่ทำงานยาวนานที่แอปพลิเคชันเริ่มและปล่อยให้ทำงานตลอดอายุโปรแกรม งานเหล่านั้นยังคงเป็นงาน create_task() gather() ใช้สำหรับรูปแบบ fan-out / fan-in: แยกงาน ทำพร้อมกัน รวมกลับ

8.4.3. ข้อยกเว้นในกลุ่ม

หากหนึ่งใน awaitable ที่รวบรวมยกข้อยกเว้น พฤติกรรมเริ่มต้นคือ ยกข้อยกเว้นออกจากการเรียก gather พี่น้องที่ยังไม่เสร็จสิ้นจะถูกยกเลิกในพื้นหลัง

นั่นมักเป็นสิ่งที่แอปพลิเคชันต้องการ งานหนึ่งใน N งานคู่ขนานล้มเหลว ดังนั้นการดำเนินการรวมก็ล้มเหลว ดังนั้นหยุดเสียเวลากับที่เหลือ บางครั้งแอปพลิเคชันต้องการสิ่งตรงกันข้าม: ให้แต่ละ awaitable เสร็จสิ้น (หรือล้มเหลว) อย่างอิสระ และตรวจสอบผลลัพธ์ภายหลัง ส่ง return_exceptions=True สำหรับกรณีนั้น:

results = await asyncio.gather(
    fetch_or_fail("ok"),
    fetch_or_fail("bad"),
    return_exceptions=True,
)
# results == ["ok-value", OSError(...)]

แต่ละรายการในรายการที่คืนมาตอนนี้เป็นค่าที่คืนมาตามปกติ หรือ ข้อยกเว้นที่ awaitable ที่สอดคล้องกันยกขึ้น ผู้เรียกตรวจสอบ isinstance(r, Exception) เพื่อแยกแยะ

8.4.4. การยกเลิก

การยกเลิก gather เอง โดยยกเลิกงานใดก็ตามที่กำลัง await อยู่ จะยกเลิก awaitable ทุกตัวที่ยังทำงานอยู่ภายใน หน้า timeouts and cancellation ครอบคลุมวิธีที่การยกเลิกแพร่กระจายผ่านสายการเรียกโดยละเอียด