מודלים של מקביליות
בתוכנה מודרנית יש לעיתים קרובות צורך לבצע משימות רבות בו-זמנית או לנהל הרבה פעולות באופן יעיל.
פייתון מספקת מספר מודלים של מקביליות:
- threading – חוטים (threads)
- multiprocessing – תהליכים נפרדים
- asyncio – תכנות אסינכרוני מבוסס אירועים (event loop)
לכל אחד מהם יתרונות, חסרונות ושימושים מתאימים.
GIL – נעילת המפרש הגלובלית
ב־CPython, ה־GIL מונע מהרצת מספר חוטים (threads) בו-זמנית על קוד פייתון.
זה מפשט את ניהול הזיכרון – אך מהווה צוואר בקבוק לתוכניות שמעמיסות על המעבד (CPU-bound).
התוצאה היא ש־threads לא יכולים לפעול במקביל אמיתי על כמה ליבות מעבד כאשר מדובר בקוד פייתון טהור.
מקביליות מבוססת חוטים (Thread-Based Concurrency)
Threads מאפשרים לתוכנית להריץ כמה זרמי פיקוד (flows) באותו תהליך.
המודול threading בפייתון מספק ממשק פשוט ליצירת חוטים.
אפשר ליצור thread ע"י:
- יצירת מופע של threading.Thread(target=func)
- או ירושה מהמחלקה Thread ומימוש המתודה run()
אבל:
חשוב להבין – בגלל ה־GIL, רק חוט אחד מריץ קוד פייתון בכל רגע נתון.
לכן:
- במשימות שתלויות ב־I/O (כמו קריאה מרשת או קובץ) – threads כן יעילים.
- במשימות כבדות למעבד (CPU-bound) – threads לא ינצלו יותר מליבה אחת, ולכן לא יועילו הרבה.
כאן, t1 ו-t2 יריצו את הפונקציות שלהם באופן שיתחלף ביניהם (interleave) – כלומר, הם ידפיסו מספרים לסירוגין.
בשל ה־GIL, הם לא ירוצו באמת בו־זמנית על מעבדים שונים, אך פייתון תעבור ביניהם בנקודות החלפת הקשר (context switch), כמו בזמן פעולות קלט/פלט או לאחר מספר מסוים של הוראות Bytecode.
ובכל זאת, הדבר עשוי לייעל משימות שתלויות בקלט/פלט (למשל קריאה משני התקנים איטיים במקביל).