TL;DR - מה ההבדלים בין תכנות concurrent לתכנות parallel?
אחד הפוסטים המוצלחים שלי עד כה הוא הסבר מפורט על שאלת הראיון האהובה עלי (ראו How does HashMap work in Java? לפרטים נוספים). לכן החלטתי ליצור סדרה של פוסטים שמפרקים שאלות נוספות. כל השאלות שיוענו בסדרה הזו שימשו בתהליך הגיוס של המעסיק הנוכחי שלי.
בפוסט של היום נתמקד ב-concurrent ו-parallel programming. אז בלי יותר דברי פתיחה, בואו נתחיל!
ההבדל בין Concurrency ל-Parallelism
למרות ש-concurrency ו-parallelism קשורים זה לזה, הם בהחלט לא אותו דבר. יש הרבה הגדרות ל-concurrency ול-parallelism. ההגדרה האישית האהובה עלי היא:
Parallelism הוא הרצת דברים ב(ובכן…) parallel, בעוד ש-concurrency היא האשליה של הרצת דברים בו-זמנית.
למרות שזו הגדרה לא מדויקת, היא נותנת הבנה טובה של המשמעות. בואו נסביר את זה עם דוגמה. נניח שרוצים להאזין למוזיקה ב-Spotify תוך כדי משחק במשחק מחשב.
אם יש לנו 2 CPUs במחשב, אנחנו יכולים להריץ את השירים שלנו על מעבד אחד בעוד שמריצים את המשחק שלנו על המעבד האחר. המחשה של הריצה תיראה כך:
כפי שרואים, כל מעבד עסוק בריצת משימה עצמאית. עכשיו נניח שיש לנו רק CPU אחד במחשב. בתרחיש כזה לא נוכל להריץ את שתי המשימות בו-זמנית כי יש לנו רק מעבד פיזי אחד במחשב. הפתרון לבעיה הזו יהיה לפרק את 2 המשימות למרווחים קטנים. אחרי שעשינו את זה, אנחנו יכולים להחליף בין השתיים על ה-CPU שלנו. אם המרווחים קטנים מספיק, מנקודת המבט של המשתמש, המשימות ירגישו כאילו הן רצות במקביל. בתרחיש הזה, הריצה תיראה כך:
בדוגמה למעלה, למערכת שלנו יש רק CPU אחד. עם זאת, הרצת ה-threads שלנו במרווחים קטנים מאפשרת למשתמש להרגיש שהם רצים בו-זמנית.
Concurrency מתקדמת
עכשיו נניח שהתקציב שלנו למחשב נמוך. אנחנו יכולים להרשות לעצמנו רק מחשב עם CPU אחד ו-thread אחד. האם עדיין אפשר להשיג ריצה concurrent?
למרות שבהתחלה השאלה הזו עשויה להישמע מסובכת, זה אכן אפשרי. כדי לפתור את הבעיה הזו, אנחנו יכולים להשתמש ב-design pattern של event-loop. הפתרון יהיה להריץ את הקוד שלנו על ה-CPU עד שמגיעים לפעולה חוסמת. פעולה חוסמת יכולה להיות קריאת דיסק (למשל, מסד נתונים) או קריאת IO (למשל, קריאת REST API). כשזה קורה, האירוע מועבר לתור blocked, והמשימה הבאה מתבצעת. אחרי timeout מסוים, האירוע מועבר חזרה לתור הביצוע ומעובד שוב.
הדיאגרמה הבאה מראה דוגמה כללית של event loop בסיסי:
למעשה, שפות מסוימות כמו JavaScript ו-NodeJS מריצות קוד אסינכרוני בדיוק על הבסיס הזה.
שימו לב שאם, לדוגמה, המשימה שלכם אף פעם לא מגיעה לפעולה חוסמת, התוכנית שלכם תתקע, ושום ריצה אחרת לא תתרחש. לדוגמה, הרצת הקוד הבא:
while (true) {
println("I'm still stuck... :(")
}סיכום
Concurrency ו-parallelism הם מושגים קשורים אך שונים. Concurrency היא היכולת לבצע הרבה משימות בו-זמנית או באותו זמן. Parallelism היא היכולת להריץ הרבה משימות בו-זמנית באמצעות מעבדים או ליבות שונות. במילים אחרות, parallelism הוא מקרה מיוחד של concurrency שבו הרבה משימות רצות בו-זמנית. יתרה מזאת, ניתן להשיג concurrency עם CPU אחד או אפילו thread אחד.