วิธีใช้ asyncio ใน Python

ฟังก์ชันการเขียนโปรแกรมแบบอะซิงโครนัสของ Python หรือเรียกสั้น ๆ ว่า async ช่วยให้คุณสามารถเขียนโปรแกรมที่ทำงานได้มากขึ้นโดยไม่ต้องรอให้งานอิสระเสร็จสิ้น asyncioห้องสมุดรวมกับงูหลามจะช่วยให้คุณมีเครื่องมือที่จะใช้สำหรับการประมวลผล async ดิสก์หรือเครือข่าย I / O โดยไม่ต้องทำทุกอย่างอื่นรอ

asyncio มีสองชนิดของ API สำหรับการรับมือกับการดำเนินงานไม่ตรงกัน:  ระดับสูง  และ  ระดับต่ำ API ระดับสูงมีประโยชน์มากที่สุดและสามารถใช้ได้กับแอปพลิเคชันที่หลากหลายที่สุด API ระดับต่ำมีประสิทธิภาพ แต่ก็ซับซ้อนและใช้ไม่บ่อย

เราจะมุ่งเน้นไปที่ API ระดับสูงในบทความนี้ ในส่วนด้านล่างนี้เราจะอธิบายถึง API ระดับสูงที่ใช้กันมากที่สุดใน  asyncioและแสดงวิธีใช้สำหรับการดำเนินการทั่วไปที่เกี่ยวข้องกับงานอะซิงโครนัส 

หากคุณยังใหม่กับ async ใน Python หรือคุณสามารถใช้การทบทวนวิธีการทำงานได้โปรดอ่านบทนำเกี่ยวกับ Python async ก่อนที่จะดำน้ำที่นี่

รันโครูทีนและงานใน Python

ตามปกติแล้วการใช้งานที่พบบ่อยที่สุดasyncioคือการเรียกใช้ส่วนอะซิงโครนัสของสคริปต์ Python ของคุณ ซึ่งหมายถึงการเรียนรู้ที่จะทำงานกับโครูทีนและงานต่างๆ 

ส่วนประกอบ async ของ Python รวมถึงโครูทีนและงานสามารถใช้ได้กับคอมโพเนนต์ async อื่น ๆ เท่านั้นไม่ใช่กับซิงโครนัส Python ทั่วไปดังนั้นคุณต้อง  asyncio เชื่อมช่องว่าง ในการดำเนินการนี้คุณใช้  asyncio.run ฟังก์ชัน:

นำเข้า asyncio

async def หลัก ():

พิมพ์ ("รอ 5 วินาที")

สำหรับ _ ในช่วง (5):

รอ asyncio.sleep (1)

พิมพ์ (".")

พิมพ์ ("รอเสร็จแล้ว")

asyncio.run (หลัก ())

สิ่งนี้จะดำเนิน  main()ไปพร้อมกับโครูทีนใด ๆ ที่  main() เริ่มทำงานและรอให้ผลลัพธ์กลับ

ตามกฎทั่วไปโปรแกรม Python ควรมีเพียง  .run() คำสั่งเดียวเช่นเดียวกับที่โปรแกรม Python ควรมีเพียง  main() ฟังก์ชันเดียว Async หากใช้อย่างไม่ระมัดระวังอาจทำให้ขั้นตอนการควบคุมของโปรแกรมอ่านยาก การมีจุดเข้าเพียงจุดเดียวไปยังรหัส async ของโปรแกรมจะช่วยป้องกันไม่ให้สิ่งต่างๆยุ่งยาก

ฟังก์ชัน Async ยังสามารถกำหนดเวลาเป็น  งานหรืออ็อบเจ็กต์ที่ห่อโครูทีนและช่วยรันได้

async def my_task ():

ทำอะไรสักอย่าง()

งาน = asyncio.create_task (my_task ())

my_task()taskจะดำเนินการแล้วในวงเหตุการณ์ที่มีผลการดำเนินงานที่เก็บไว้ใน 

หากคุณมีงานเพียงงานเดียวที่ต้องการให้ได้ผลลัพธ์คุณสามารถใช้  asyncio.wait_for(task) เพื่อรอให้งานเสร็จสิ้นจากนั้นใช้  task.result() เพื่อดึงผลลัพธ์ออกมา แต่ถ้าคุณกำหนดเวลาให้ดำเนินการหลายอย่างและคุณต้องการรอให้งาน  ทั้งหมด  เสร็จสิ้นให้ใช้  asyncio.wait([task1, task2]) เพื่อรวบรวมผลลัพธ์ (โปรดทราบว่าคุณสามารถกำหนดระยะหมดเวลาสำหรับการดำเนินการได้หากคุณไม่ต้องการให้การดำเนินการเหล่านี้ทำงานเกินระยะเวลาที่กำหนด)

จัดการ async event loop ใน Python

ควบคุมการใช้งานทั่วไปสำหรับ  asyncio คือการจัดการ async  ห่วงเหตุการณ์ ห่วงเหตุการณ์เป็นวัตถุที่เรียกใช้ฟังก์ชัน async และการเรียกกลับ asyncio.run()มันสร้างขึ้นโดยอัตโนมัติเมื่อคุณใช้ โดยทั่วไปคุณต้องการใช้ async event loop เพียงครั้งเดียวต่อโปรแกรมอีกครั้งเพื่อให้จัดการสิ่งต่างๆได้

หากคุณกำลังเขียนซอฟต์แวร์ขั้นสูงขึ้นเช่นเซิร์ฟเวอร์คุณจะต้องมีสิทธิ์เข้าถึงระดับล่างเพื่อไปยังลูปเหตุการณ์ ด้วยเหตุนี้คุณสามารถ "ยกฝากระโปรง" และทำงานโดยตรงกับภายในของลูปเหตุการณ์ แต่สำหรับงานง่ายๆคุณไม่จำเป็นต้องทำ

อ่านและเขียนข้อมูลด้วยสตรีมใน Python

สถานการณ์ที่ดีที่สุดสำหรับ async คือการดำเนินการเครือข่ายที่ใช้งานมานานโดยที่แอปพลิเคชันอาจบล็อกการรอให้ทรัพยากรอื่นส่งคืนผลลัพธ์ ด้วยเหตุ  asyncio นี้จึงเสนอสตรีมซึ่งเป็นกลไกระดับสูงสำหรับการดำเนินการ I / O เครือข่าย ซึ่งรวมถึงการทำหน้าที่เป็นเซิร์ฟเวอร์สำหรับคำขอเครือข่าย

asyncio ใช้สองคลาส  StreamReader และ  StreamWriterเพื่ออ่านและเขียนจากเครือข่ายในระดับสูง หากคุณต้องการอ่านจากเครือข่ายคุณจะใช้  asyncio.open_connection() เพื่อเปิดการเชื่อมต่อ ฟังก์ชันนั้นจะส่งคืนทูเพิล  StreamReader และ  StreamWriter อ็อบเจกต์และคุณจะใช้  .read() และ  .write() เมธอดในแต่ละรายการเพื่อสื่อสาร

ที่จะได้รับการเชื่อมต่อจากโฮสต์ระยะไกล, asyncio.start_server()การใช้งาน asyncio.start_server()ฟังก์ชั่นใช้เวลาเป็นอาร์กิวเมนต์ฟังก์ชั่นการโทรกลับ,  client_connected_cbซึ่งเรียกว่าเมื่อใดก็ตามที่ได้รับการร้องขอ ฟังก์ชันเรียกกลับนั้นรับอินสแตนซ์  StreamReader และStreamWriter เป็นอาร์กิวเมนต์ดังนั้นคุณสามารถจัดการตรรกะการอ่าน / เขียนสำหรับเซิร์ฟเวอร์ได้ (ดูตัวอย่างเซิร์ฟเวอร์ HTTP แบบธรรมดาที่ใช้   ไลบรารีasyncio-driven  aiohttpได้ที่นี่)

ซิงโครไนซ์งานใน Python

งานอะซิงโครนัสมักจะทำงานแยกกัน แต่บางครั้งคุณอาจต้องการให้งานเหล่านั้นสื่อสารกัน asyncio จัดเตรียมคิวและกลไกอื่น ๆ สำหรับการซิงโครไนซ์ระหว่างงาน:

  • คิวasyncio คิวอนุญาตให้ฟังก์ชันอะซิงโครนัสจัดเรียงวัตถุ Python ที่จะใช้โดยฟังก์ชัน async อื่น ๆ ตัวอย่างเช่นเพื่อกระจายปริมาณงานระหว่างฟังก์ชันประเภทต่างๆตามพฤติกรรม
  • การซิงโครไนซ์แบบดั้งเดิม : ล็อกเหตุการณ์เงื่อนไขและเซมาโฟร์ในการasyncioทำงานเหมือนกับคู่หู Python ทั่วไป 

สิ่งหนึ่งที่ควรทราบเกี่ยวกับวิธีการเหล่านี้ทั้งหมดก็คือวิธีนี้  ไม่  ปลอดภัยต่อเธรด นี่ไม่ใช่ปัญหาสำหรับงาน async ที่ทำงานในลูปเหตุการณ์เดียวกัน แต่ถ้าคุณพยายามแชร์ข้อมูลกับงานในลูปเหตุการณ์เธรดระบบปฏิบัติการหรือกระบวนการอื่นคุณจะต้องใช้  threading โมดูลและวัตถุในการทำเช่นนั้น

นอกจากนี้หากคุณต้องการเรียก  ใช้โครูทีน  ข้ามขอบเขตเธรดให้ใช้  asyncio.run_coroutine_threadsafe() ฟังก์ชันและส่งผ่านเหตุการณ์ลูปเพื่อใช้เป็นพารามิเตอร์

หยุดโครูทีนชั่วคราวใน Python

การใช้งานทั่วไปอีกอย่างหนึ่งของการใช้งานทั่วไป  asyncioและการพูดคุยที่ไม่ได้รับการกล่าวถึงคือการรอให้มีระยะเวลาโดยพลการภายในโครูทีน คุณไม่สามารถใช้  time.sleep() สำหรับสิ่งนี้หรือคุณจะบล็อกทั้งโปรแกรม ให้ใช้แทน  asyncio.sleep()ซึ่งทำให้โครูทีนอื่น ๆ ทำงานต่อไปได้

ใช้ async ระดับล่างใน Python

สุดท้ายหากคุณคิดว่าแอปที่คุณกำลังสร้างอาจต้องใช้asyncioส่วนประกอบระดับล่างลองดูรอบ ๆ ก่อนที่คุณจะเริ่มเขียนโค้ด: มีโอกาสดีที่จะมีคนสร้างไลบรารี Python แบบ async ที่ทำในสิ่งที่คุณต้องการแล้ว

ตัวอย่างเช่นถ้าคุณต้องการสอบถาม DNS async ตรวจสอบ  aiodns ห้องสมุดและสำหรับการประชุม async SSH asyncSSHมี ค้นหา PyPI ด้วยคีย์เวิร์ด“ async” (รวมถึงคีย์เวิร์ดอื่น ๆ ที่เกี่ยวข้องกับงาน) หรือตรวจสอบรายการ Awesome Asyncio ที่คัดสรรด้วยมือเพื่อดูแนวคิด