יסודות Goroutines
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 59 מתוך 107.
goroutine הוא תהליכון קל משקל המנוהל על ידי סביבת הריצה של Go. בניגוד לתהליכונים מסורתיים שצורכים כמות משמעותית של זיכרון, goroutine מתחיל עם כמה קילובייטים בלבד של שטח מחסנית, מה שמאפשר לך להריץ אלפים מהם במקביל.
כדי להתחיל goroutine, פשוט הציבו את מילת המפתח go לפני קריאה לפונקציה:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // פועל במקביל
go sayHello("Bob") // פועל במקביל
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}כאשר go sayHello("Alice") מתבצע, הוא לא ממתין עד ש־sayHello תסתיים. במקום זאת, הוא ממשיך מיד לשורה הבאה בזמן ש־sayHello פועלת ברקע. לכן אנחנו צריכים את time.Sleep; בלעדיו, main הייתה מסתיימת לפני שה־goroutines יושלמו.
אפשר גם להפעיל גורוטינות באמצעות פונקציות אנונימיות:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")הפונקציה הראשית עצמה רצה בתוך goroutine. כשהיא מסתיימת, כל שאר ה-goroutine מופסקות מיד. הן לא מקבלות הזדמנות לסיים. שימוש ב-time.Sleep הוא פתרון זמני; בשיעורים הבאים תלמדו טכניקות סנכרון מתאימות באמצעות ערוצים וקבוצות המתנה.
אתגר
קלבואו נבנה מנהל משימות שמבצע כמה משימות במקביל באמצעות goroutines. תיצרו מערכת פשוטה שבה המשימות רצות ברקע בזמן שהתוכנית הראשית ממשיכה לפעול, וכך תדגימו כיצד goroutines מאפשרות ביצוע במקביל.
תארגנו את הקוד שלכם בשני קבצים:
tasks.go: הגדירו את לוגיקת ביצוע המשימות.צרו struct בשם
Taskעם השדותName(מחרוזת) ו-Duration(מספר שלם, המייצג אלפיות שנייה).ממשו מתודה
Run()עבורTask, שמדמה עבודה באמצעות השהיה למשך הזמן של המשימה, ואז מדפיסה:Task [Name] completedצרו פונקציה
RunAllTasks(tasks []Task)שמפעילה את המתודהRun()של כל משימה כ-goroutine. לאחר הפעלת כל ה-goroutines, עליה להמתין 500 אלפיות שנייה כדי לאפשר להן להסתיים, ואז להדפיס:All tasks launchedmain.go: קראו את פרטי המשימות ותזמרו את הביצוע במקביל.קראו את מספר המשימות, ואז קראו את השם ואת משך הזמן של כל משימה. צרו את המשימות והעבירו אותן אל
RunAllTasks. לאחר שהיא תחזור, הדפיסו:Main function done
הקלטים הבאים יסופקו:
- שורה 1: מספר המשימות (מספר שלם)
- השורות הבאות: עבור כל משימה, שתי שורות — שם המשימה, ואז משך הזמן שלה באלפיות שנייה
לדוגמה, בהינתן:
3
Download
100
Process
50
Upload
75הפלט שלכם צריך להציג את השלמת המשימות (הסדר עשוי להשתנות עקב הביצוע במקביל), ולאחר מכן את הודעות ההשלמה:
Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function doneהערה: מכיוון ש-goroutines פועלות במקביל, משימות שמשך הזמן שלהן קצר יותר מסתיימות בדרך כלל ראשונות. סדר השלמת המשימות תלוי במשך הזמן שלהן — משימות קצרות יותר מסתיימות לפני משימות ארוכות יותר.
השתמשו ב-time.Sleep עם time.Duration ו-time.Millisecond עבור ההשהיות. זכרו שללא ההשהיה בתוך RunAllTasks, הפונקציה תחזור לפני שה-goroutines יסיימו את עבודתן.
נסו בעצמכם
package main
import (
"fmt"
)
func main() {
// קרא את מספר המשימות
var numTasks int
fmt.Scanln(&numTasks)
// TODO: צור slice לשמירת המשימות
// TODO: קרא את השם ומשך הזמן של כל משימה
// עבור כל משימה:
// - קרא את שם המשימה
// - קרא את משך הזמן (כמספר שלם)
// - צור Task והוסף אותו ל-slice שלך
// TODO: קרא ל-RunAllTasks עם ה-slice של המשימות שלך
// TODO: הדפס "Main function done"
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין