3 צעדים לשיפוץ אסינכרון של פייתון

פייתון הוא אחד מבין השפות הרבות שתומכות בדרך כלשהי לכתוב תוכניות אסינכרוניות - תוכניות שעוברות באופן חופשי בין מספר משימות, שכולן פועלות בבת אחת, כך שאף משימה לא תחזיק את ההתקדמות של האחרות.

עם זאת, רוב הסיכויים שכתבת תוכניות פייתון סינכרוניות - תוכניות שעושות רק דבר אחד בכל פעם, ומחכות שכל משימה תסתיים לפני שתתחיל אחרת. המעבר לאסינכרון יכול להיות מטלטל, מכיוון שהוא דורש ללמוד לא רק תחביר חדש, אלא גם דרכי חשיבה חדשות על הקוד של האדם. 

במאמר זה נחקור כיצד ניתן להפוך תוכנית סינכרונית קיימת לתוכנית אסינכרונית. זה כולל לא רק קישוט פונקציות עם תחביר אסינכרון; זה גם מחייב לחשוב אחרת על האופן שבו התוכנית שלנו פועלת, ולהחליט אם אסינכרון הוא אפילו מטאפורה טובה למה שהיא עושה. 

[עוד בנושא: למד טיפים וטריקים של פייתון מסרטוני הפיתון החכמים של סרדר יגולאלפ]

מתי להשתמש בסינכרון בפייתון

תוכנית Python מתאימה ביותר לאסינכרון כאשר יש לה את המאפיינים הבאים:

  • זה מנסה לעשות משהו שמקושר בעיקר על ידי קלט / פלט או על ידי המתנה לסיום תהליך חיצוני כלשהו, ​​כמו קריאת רשת ארוכת שנים.
  • זה מנסה לבצע אחת או יותר מסוג זה של משימות בבת אחת, ואולי גם לטפל באינטראקציות של משתמשים.
  • המשימות המדוברות אינן כבדות מבחינה חישובית.

תוכנית פייתון המשתמשת בשרשור היא בדרך כלל מועמדת טובה לשימוש ב- async. החוטים בפייתון משתפים פעולה; הם נכנעים זה לזה לפי הצורך. משימות אסינכרון בפייתון פועלות באותה צורה. בנוסף, async מציע יתרונות מסוימים על פני אשכולות:

  • async/ awaitתחביר מקלה לזהות את החלקים אסינכרוני של התוכנית שלך. לעומת זאת, לעתים קרובות קשה לדעת במבט חטוף אילו חלקים באפליקציה פועלים בשרשור. 
  • מכיוון שמשימות אסינכרון חולקות את אותו השרשור, כל הנתונים שאליהם הם ניגשים מנוהלים אוטומטית על ידי ה- GIL (המנגנון המקורי של פיתון לסינכרון גישה לאובייקטים). חוטים דורשים לרוב מנגנונים מורכבים לסנכרון. 
  • משימות Async קלות יותר לניהול וביטול מאשר אשכולות.

השימוש ב- async אינו מומלץ אם תוכנית הפייתון שלך כוללת את המאפיינים הבאים:

  • למשימות עלות חישוב גבוהה - למשל, הן עושות ריסוק כבד של מספרים. ניתן לטפל בצורה הטובה ביותר בעבודה חישובית כבדה multiprocessingהמאפשרת להקדיש חוט חומרה שלם לכל משימה.
  • המשימות אינן מרוויחות מהשתלבות. אם כל משימה תלויה במשימה האחרונה, אין טעם לגרום להן לפעול בצורה אסינכרונית. עם זאת, אם התוכנית כוללת  קבוצות של משימות סדרתיות, תוכל להריץ כל קבוצה בצורה אסינכרונית.

שלב 1: זהה את החלקים הסינכרוניים והאסינכרוניים של התוכנית שלך

קוד async של Python צריך להיות מופעל על ידי החלקים הסינכרוניים של יישום ה- Python שלך ומנוהל עליו. לשם כך, המשימה הראשונה שלך בעת המרת תוכנית לסינכרון היא לצייר קו בין חלקי הסינכרון לבין הסינכרון של הקוד שלך.

במאמר הקודם שלנו בנושא async, השתמשנו באפליקציית מגרדים באינטרנט כדוגמה פשוטה. החלקים הסינכרוניים של הקוד הם השגרה הפותחת את חיבורי הרשת וקוראת מהאתר - כל מה שאתה רוצה לשלב. אבל החלק של התוכנית שמתחיל את כל זה לא מסונכרן; זה מפעיל את משימות הסינכרון ואז סוגר אותן בחן כשהן מסיימות.

חשוב גם להפריד בין כל פעולה שעלולה  לחסום לבין async, ולהשאיר אותה בחלק הסנכרון של האפליקציה שלך. קריאת קלט משתמשים מהקונסולה, למשל, חוסמת הכל כולל לולאת האירועים async. לכן, אתה רוצה לטפל בקלט משתמשים לפני שתפעיל משימות אסינכרון או לאחר שתסיים אותן. (זה הוא אפשרי קלט משתמש ידית אסינכרוני באמצעות multiprocessing או שחלה, אבל זה תרגיל מתקדם אנו לא ניכנס כאן.)

כמה דוגמאות לפעולות חסימה:

  • קלט קונסולה (כפי שתיארנו זה עתה).
  • משימות הכרוכות בניצול כבד של המעבד.
  • משתמש time.sleepבכוח להשהות. שים לב שאתה יכול לישון בתוך פונקציית async על ידי שימוש asyncio.sleepכתחליף ל time.sleep.

שלב 2: המרת פונקציות סינכרון מתאימות לפונקציות אסינכרון

ברגע שאתה יודע אילו חלקים בתוכנית שלך יפעלו באופן אסינכרוני, תוכל לחלק אותם לפונקציות (אם עדיין לא עשית זאת) ולהפוך אותם לפונקציות אסינכרוניות עם asyncמילת המפתח. לאחר מכן יהיה עליך להוסיף קוד לחלק הסינכרוני של היישום שלך כדי להריץ את קוד הסינכרון ולאסוף תוצאות ממנו במידת הצורך.

הערה: תרצה לבדוק את שרשרת השיחות של כל פונקציה שביצעת אסינכרוני, ולוודא שהן לא קוראות לפעולה ארוכת טווח או חסימה. פונקציות Async יכולות להתקשר ישירות לפונקציות סינכרון, ואם פונקציית סינכרון זו נחסמת, כך גם פונקציית async קוראת לה.

בואו נסתכל על דוגמה פשוטה לאופן שבו המרת סנכרון לאסינכרון עשויה לעבוד. הנה התוכנית "לפני" שלנו:

def a_function (): # פעולה כלשהי תואמת async שלוקחת זמן def another_function (): # פונקציית סינכרון כלשהי, אך לא חסימת def_duff (): a_function () פונקציה אחרת () def main (): עבור _ בטווח (3): do_stuff () ראשי () 

אם אנו רוצים ששלושה מקרים do_stuffיפעלו כמשימות אסינכרון, עלינו להפוך do_stuff (ואולי כל מה שהוא נוגע אליו) לקוד אסינכרון. הנה מעבר ראשון בהמרה:

ייבא asyncio async def a_function (): # פעולה תואמת async שנמשכת זמן def אחר_function (): # פונקציית סינכרון כלשהי, אך לא חסימת def async do_stuff (): ממתין a_function () פונקציה אחרת () async def main () ): משימות = [] עבור _ בטווח (3): משימות.תוסף (asyncio.create_task (do_stuff ())) ממתינים asyncio.gather (משימות) asyncio.run (main ()) 

שימו לב לשינויים שעשינו בהם  main. main משתמש כעת asyncioבהשקת כל מופע do_stuffכמשימה במקביל, ואז ממתין לתוצאות ( asyncio.gather). הפכנו גם a_functionלפונקציה async מכיוון שאנחנו רוצים שכל המקרים a_functionיפעלו זה לצד זה ולצד כל פונקציות אחרות שזקוקות להתנהגות אסינכרון.

אם היינו רוצים ללכת צעד קדימה, נוכל גם להמיר another_functionלאסינכרון:

async def אחר_פונקציה (): # פונקציית סינכרון כלשהי, אך לא חסימה אחת async def do_stuff (): מחכה לפונקציה__ () מחכה לפונקציה אחרת () 

עם זאת, ביצוע  another_function אסינכרוני יהיה יתר על המידה, מכיוון (כפי שציינו) זה לא עושה שום דבר שיחסום את התקדמות התוכנית שלנו. כמו כן, אם יתקשרו חלקים סינכרוניים של התוכנית שלנו  another_function, נצטרך להמיר אותם גם לאסינכרון, מה שעלול להפוך את התוכנית שלנו למסובכת ממה שהיא צריכה להיות.

שלב 3: בדוק את תוכנית האסינכרון של פייתון ביסודיות

כל בדיקה של כל תכנית שהמרה אסינכרון צריכה להיבדק לפני שהיא נכנסת לייצור כדי להבטיח שהיא פועלת כמצופה.

אם התוכנית שלך היא צנועה בגודלה - נניח, כמה תריסר שורות בערך - ואינה זקוקה לחבילת בדיקה מלאה, אז זה לא צריך להיות קשה לוודא שהיא פועלת כמתוכנן. עם זאת, אם אתה ממיר את התוכנית לאסינכרון כחלק מפרויקט גדול יותר, שבו חבילת בדיקה היא מתקן סטנדרטי, הגיוני לכתוב בדיקות יחידה לרכיבי אסינכרון וסינכרון כאחד.

שתי מסגרות הבדיקה העיקריות בפייתון כוללות כעת תמיכה כלשהי בסינכרון. unittest המסגרת של פייתון עצמה  כוללת אובייקטים של מבחן מקרה לפונקציות אסינכרון pytestומציעה  pytest-asyncioלאותם מטרות.

לבסוף, בעת כתיבת מבחנים לרכיבי אסינכרון, יהיה עליכם להתמודד עם האסינכרוניות מאוד שלהם כתנאי לבדיקות. לדוגמא, אין כל ערובה כי עבודות אסינכרון יושלמו לפי ההזמנה שהוגשו. הראשון עשוי לבוא אחרון, וחלקם לעולם לא יושלמו כלל. כל בדיקות שתעצב לפונקציה async חייבות לקחת בחשבון את האפשרויות הללו.

איך לעשות יותר עם פייתון

  • התחל לעבוד עם אסינכרון בפייתון
  • כיצד להשתמש ב- asyncio בפייתון
  • כיצד להשתמש ב- PyInstaller ליצירת הפעלות של Python
  • מדריך Cython: כיצד להאיץ את Python
  • כיצד להתקין את פייתון בדרך החכמה
  • כיצד לנהל פרויקטים של פייתון באמצעות שירה
  • כיצד לנהל פרויקטים של פייתון באמצעות Pipenv
  • Virtualenv ו- venv: הסברים על סביבות וירטואליות של Python
  • Python virtualenv ו- venv do's and donts
  • הסבר על השחלת פיתון ותהליכי משנה
  • כיצד להשתמש בבאגים של פייתון
  • כיצד להשתמש ב- timeit לפרופיל קוד פייתון
  • כיצד להשתמש ב- cProfile לפרופיל קוד פייתון
  • כיצד להמיר את פייתון ל- JavaScript (ובחזרה)