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 ครอบคลุมวิธีที่การยกเลิกแพร่กระจายผ่านสายการเรียกโดยละเอียด