שיעורי מודול (1/5)
Goroutines: מקביליות קלת משקל
goroutine היא יחידה של ביצוע במקביל המנוהלת על ידי ה-Go
זמן ריצה. אתה מפעיל אחד עם מילת המפתח go לפני פונקציה
להתקשר:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()ההצהרה go חוזרת מיד: הגורוטין מתחיל ב
במקביל והמתקשר ממשיך.
למה הם "קלי משקל"
- מחסנית ראשונית ~2KB (גדל באופן דינמי, עד מאות MB במידת הצורך).
- משולבת על מספר קטן של שרשורי מערכת הפעלה לפי זמן הריצה (
GOMAXPROCS). - אלפי/מיליוני גורוטינים הם דבר שבשגרה; עם שרשורי מערכת הפעלה הם יהיו בלתי אפשריים.
מבחינה קונספטואלית זה "אש ושכח במקביל", אבל עם כל
כלים אידיומטיים לתיאום (ערוצים, sync.WaitGroup,
context).
ראשי לא מחכה
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}כאשר main חוזר, התהליך מסתיים: גורוטינים לא מקבלים
כדי "לסיים את עבודתם", הם פשוט נהרגים יחד עם
תהליך. נדרש סנכרון מפורש.
סנכרון: תצוגה מקדימה
שלושת הכלים שתראו בשיעורים הבאים:
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
work()
done <- struct{}{}
}()
<-done // attendi
// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()
// 3) context: cancellazione/timeout propagatiבאג הסגירה ה"קלאסי".
עד Go 1.21 זה היה תקלה תכופה:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}החל מ-עבור 1.22 המשתנה for נוצר לאחרונה בכל אחד מהם
איטרציה, כך שהבאג תוקן כברירת מחדל. בבסיסי קוד ישנים יותר תעשה זאת
עדיין רואים את דפוס התיקון:
for i := 0; i < 3; i++ {
i := i // shadow esplicito
go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
go func(n int) { fmt.Println(n) }(i)
}Goroutine != שרשור מערכת ההפעלה
| היבט | Goroutine | שרשור מערכת הפעלה |
|---|---|---|
| מחסנית ראשונית | ~2 KB | 1–8 מגה-בייט (מוקצה בהתחלה) |
| יצירה | ננו-שניות | מיקרו שניות |
| תזמון | עבור זמן ריצה (שיתופיות) | קרנל (מנע) |
| ספירה מעשית | 100K+ | כמה אלפים |
שרשור מערכת הפעלה בודד יכול לארח גורוטיינים רבים. זמן הריצה זז גורוטינים על פני חוטים בעת הצורך.
נסה את זה
הפעל את פונקציית הברכה כגורוטינה והמתן 100 אלפיות השנייה עם הזמן. שינה לפני היציאה הראשית.
הצג רמז
'גו' לפני קריאת פונקציה מפעיל את הגורוטין.
הפתרון זמין לאחר 3 ניסיונות
הפעל פונקציה אנונימית כגורוטינה שמדפיסה 'היי'.
הצג רמז
פונקציה אנונימית נקראת עם `()` עוקב: `go func(){...}()`.
הפתרון זמין לאחר 3 ניסיונות
מה קורה אם 'ראשי' יוצא לפני שגורוטינה מסתיימת?
func main() {
go work()
// main ritorna immediatamente
}לְסַכֵּם
go f()משגר את f() במקביל; זה חוזר מיד.- Goroutine = ~2 KB ראשוני, מרובב על כמה שרשורי מערכת הפעלה.
mainלא ממתין: אתה צריך סנכרון (ערוץ/קבוצת המתנה/הקשר).time.Sleepהוא דידקטי בלבד, לעולם לא לסנכרון בהפקה.- מ-Go 1.22 המשתנה
forהוא "טרי" בכל איטרציה (לא עוד באג "3 3 3").