3 ขั้นตอนในการยกเครื่อง Python async

Python เป็นหนึ่งในหลายภาษาที่รองรับวิธีการเขียนโปรแกรมแบบอะซิงโครนัส - โปรแกรมที่สลับระหว่างงานหลาย ๆ งานได้อย่างอิสระทั้งหมดทำงานพร้อมกันเพื่อให้ไม่มีงานใดรองรับความคืบหน้าของงานอื่น ๆ

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

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

[เพิ่มเติมที่: เรียนรู้เคล็ดลับและเทคนิค Python จากวิดีโอ Smart Python ของ Serdar Yegulalp]

เมื่อใดควรใช้ async ใน Python

โปรแกรม Python เหมาะที่สุดสำหรับ async เมื่อมีคุณสมบัติดังต่อไปนี้:

  • พยายามทำบางสิ่งที่ส่วนใหญ่ถูกผูกไว้โดย I / O หรือโดยรอให้กระบวนการภายนอกบางอย่างเสร็จสมบูรณ์เช่นเครือข่ายที่ใช้งานมานานอ่าน
  • มันพยายามทำงานประเภทนั้นอย่างน้อยหนึ่งอย่างพร้อมกันในขณะที่อาจจัดการการโต้ตอบของผู้ใช้ด้วย
  • งานที่เป็นปัญหาไม่หนักในเชิงคำนวณ

โปรแกรม Python ที่ใช้เธรดมักเป็นตัวเลือกที่ดีสำหรับการใช้ async เธรดใน Python ร่วมมือกัน พวกเขายอมจำนนต่อกันและกันตามความจำเป็น งาน Async ใน Python ทำงานในลักษณะเดียวกัน นอกจากนี้ async ยังมีข้อดีมากกว่าเธรด:

  • async/ awaitไวยากรณ์ทำให้ง่ายต่อการระบุชิ้นส่วนที่ไม่ตรงกันของโปรแกรมของคุณ ในทางตรงกันข้ามมักจะยากที่จะบอกได้อย่างรวดเร็วว่าส่วนใดของแอปที่ทำงานในเธรด 
  • เนื่องจากงาน async ใช้เธรดเดียวกันข้อมูลใด ๆ ที่เข้าถึงจะถูกจัดการโดยอัตโนมัติโดย GIL (กลไกดั้งเดิมของ Python สำหรับการซิงโครไนซ์การเข้าถึงวัตถุ) เธรดมักต้องการกลไกที่ซับซ้อนในการซิงโครไนซ์ 
  • งาน Async นั้นจัดการและยกเลิกได้ง่ายกว่าเธรด

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

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

ขั้นตอนที่ 1: ระบุส่วนซิงโครนัสและอะซิงโครนัสของโปรแกรมของคุณ

โค้ด Python async จะต้องเปิดใช้และจัดการโดยส่วนซิงโครนัสของแอปพลิเคชัน Python ของคุณ ด้วยเหตุนี้งานแรกของคุณในการแปลงโปรแกรมเป็น async คือการลากเส้นระหว่างส่วนซิงค์และ async ของโค้ดของคุณ

ในบทความก่อนหน้านี้เกี่ยวกับ async เราใช้แอปพลิเคชัน web scraper เป็นตัวอย่างง่ายๆ ส่วน async ของโค้ดคือกิจวัตรที่เปิดการเชื่อมต่อเครือข่ายและอ่านจากไซต์ - ทุกอย่างที่คุณต้องการแทรกสอด แต่ส่วนหนึ่งของโปรแกรมที่เริ่มต้นทั้งหมดนั้นไม่ใช่ async มันเปิดงาน async จากนั้นปิดงานอย่างสง่างามเมื่อเสร็จสิ้น

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

ตัวอย่างบางส่วนของการดำเนินการบล็อก:

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

ขั้นตอนที่ 2: แปลงฟังก์ชันการซิงค์ที่เหมาะสมเป็นฟังก์ชัน async

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

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

ลองดูตัวอย่างง่ายๆว่าการแปลง sync-to-async อาจทำงานอย่างไร นี่คือโปรแกรม“ ก่อน” ของเรา:

def a_function (): # การกระทำบางอย่างที่เข้ากันได้กับ async ที่ใช้เวลาสักครู่ def another_function (): # ฟังก์ชั่นการซิงค์บางอย่าง แต่ไม่บล็อก def do_stuff (): a_function () another_function () def main (): สำหรับ _ ในช่วง (3): do_stuff () หลัก () 

หากเราต้องการให้สามอินสแตนซ์do_stuffทำงานเป็นงาน async เราจำเป็นต้องเปลี่ยนdo_stuff (และทุกอย่างที่สัมผัสได้) ให้เป็นรหัส async นี่คือบัตรผ่านแรกในการแปลง:

นำเข้า asyncio async def a_function (): # การดำเนินการที่เข้ากันได้กับ async บางอย่างที่ใช้เวลาสักครู่ def another_function (): # ฟังก์ชั่นการซิงค์บางอย่าง แต่ไม่ใช่การปิดกั้น async def do_stuff (): รอ a_function () another_function () async def main ( ): งาน = [] สำหรับ _ ในช่วง (3): task.append (asyncio.create_task (do_stuff ())) รอ asyncio.gather (งาน) asyncio.run (main ()) 

mainหมายเหตุการเปลี่ยนแปลงที่เราทำเพื่อ ตอนนี้main ใช้asyncioเพื่อเรียกใช้แต่ละอินสแตนซ์ของdo_stuffงานพร้อมกันจากนั้นรอผลลัพธ์ ( asyncio.gather) นอกจากนี้เรายังแปลงa_functionเป็นฟังก์ชัน async เนื่องจากเราต้องการให้อินสแตนซ์ทั้งหมดa_functionทำงานเคียงข้างกันและควบคู่ไปกับฟังก์ชันอื่น ๆ ที่ต้องการลักษณะการทำงานแบบ async

หากเราต้องการก้าวไปอีกขั้นเราสามารถแปลงanother_functionเป็น async:

async def another_function (): # ฟังก์ชั่นการซิงค์บางอย่าง แต่ไม่ใช่การปิดกั้น async def do_stuff (): รอ a_function () รอ another_function () 

อย่างไรก็ตามการทำ  another_function แบบอะซิงโครนัสจะเป็นการใช้งานมากเกินไปเนื่องจาก (ตามที่เราได้ระบุไว้) จะไม่ทำสิ่งใดที่จะขัดขวางความคืบหน้าของโปรแกรมของเรา นอกจากนี้หากมีการเรียกส่วนซิงโครนัสในโปรแกรมของ  another_functionเราเราจะต้องแปลงเป็น async ด้วยซึ่งอาจทำให้โปรแกรมของเราซับซ้อนกว่าที่จำเป็น

ขั้นตอนที่ 3: ทดสอบโปรแกรม Python async ของคุณอย่างละเอียด

โปรแกรมใด ๆ ที่แปลงเป็น async จะต้องได้รับการทดสอบก่อนที่จะเข้าสู่การผลิตเพื่อให้แน่ใจว่าทำงานได้ตามที่คาด

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

เฟรมเวิร์กการทดสอบหลักทั้งสองใน Python ตอนนี้มีการรองรับ async บางประเภท unittest เฟรมเวิร์กของ Python  มีออบเจ็กต์กรณีทดสอบสำหรับฟังก์ชัน async และpytestข้อเสนอ  pytest-asyncioสำหรับจุดจบเดียวกัน

Finally, when writing tests for async components, you’ll need to handle their very asynchronousness as a condition of the tests. For instance, there is no guarantee that async jobs will complete in the order they were submitted. The first one might come in last, and some might never complete at all. Any tests you design for an async function must take these possibilities into account.

How to do more with Python

  • Get started with async in Python
  • How to use asyncio in Python
  • How to use PyInstaller to create Python executables
  • Cython tutorial: How to speed up Python
  • How to install Python the smart way
  • How to manage Python projects with Poetry
  • How to manage Python projects with Pipenv
  • Virtualenv and venv: Python virtual environments explained
  • Python virtualenv and venv do’s and don’ts
  • Python threading and subprocesses explained
  • How to use the Python debugger
  • วิธีใช้ timeit to profile Python code
  • วิธีใช้ cProfile เพื่อสร้างโปรไฟล์โค้ด Python
  • วิธีแปลง Python เป็น JavaScript (และกลับมาอีกครั้ง)