מודלים של מקביליות
בתוכנה מודרנית יש לעיתים קרובות צורך לבצע משימות רבות בו-זמנית או לנהל הרבה פעולות באופן יעיל.
פייתון מספקת מספר מודלים של מקביליות:
- threading – חוטים (threads)
- multiprocessing – תהליכים נפרדים
- asyncio – תכנות אסינכרוני מבוסס אירועים (event loop)
לכל אחד מהם יתרונות, חסרונות ושימושים מתאימים.
GIL – נעילת המפרש הגלובלית
ב־CPython, ה־GIL מונע מהרצת מספר חוטים (threads) בו-זמנית על קוד פייתון.
זה מפשט את ניהול הזיכרון – אך מהווה צוואר בקבוק לתוכניות שמעמיסות על המעבד (CPU-bound).
התוצאה היא ש־threads לא יכולים לפעול במקביל אמיתי על כמה ליבות מעבד כאשר מדובר בקוד פייתון טהור.
בטיחות חוטים (Thread Safety)
כאשר מספר חוטים פועלים במקביל וניגשים לנתונים משותפים – חייבים להבטיח גישה בטוחה, אחרת עלולים להיווצר מצבי תחרות (race conditions).
פייתון מספקת כלים לסנכרון כמו:
- threading.Lock (נעילה בסיסית)
- RLock (נעילה חוזרת)
- Semaphore
- Event
- Condition
בדוגמה זו, אנו דואגים שרק חוט אחד בכל פעם יוכל לשנות את המשתנה shared counter ע"י שימוש ב־lock.
ללא נעילה, עלולות להתרחש התנגשות בין חוטים והעדכונים לא יישמרו כראוי.
ThreadPoolExecutor – תזמון פשוט של משימות מקביליות
למשימות פשוטות יותר, ניתן להשתמש ב־concurrent .futures ,ThreadPoolExecutor
המאפשר לשלוח משימות (callables) לבריכת חוטים ולקבל תוצאה באמצעות Future.
מתי להשתמש בחוטים?
- כאשר יש משימות שתלויות בקלט/פלט (I/O-bound)
- כאשר נדרשת תגובתיות (למשל ב־GUI או שרת שמטפל בבקשות)
- לא מתאים למשימות כבדות למעבד (CPU-bound) – לשם כך עדיף להשתמש בתהליכים (multiprocessing) או בקוד חיצוני (C/Cython) שמשחרר את ה־GIL.