Menu
Coddy logo textTech

פקודת Select

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 62 מתוך 107.

הפקודה select מאפשרת ל-goroutine להמתין בו-זמנית למספר פעולות בערוצים. היא דומה לפקודת switch, אבל כל מקרה כולל שליחה או קבלה בערוץ. כשכמה ערוצים מוכנים, select בוחרת אחד מהם באקראי.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from channel 1" }()
    go func() { ch2 <- "from channel 2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

ללא select, קבלה מ־ch1 תחסום עד להגעת נתונים, ועלולה לגרום להחמצת נתונים ב־ch2. עם select, הערוץ שבו מתקבלים נתונים ראשון מטופל.

מקרה ה־default הופך את select ללא חוסם. הוא מתבצע מיד אם אין ערוצים מוכנים:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

תבנית נפוצה משלבת את select עם פסקי זמן באמצעות time.After, שמחזירה ערוץ שמקבל ערך לאחר פרק הזמן שצוין:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

כך התוכנית שלך לא תמתין לנצח כשפעולה בערוץ עלולה לא להסתיים לעולם.

challenge icon

אתגר

קל

בואו נבנה צג שירות שמשתמש בהוראת select כדי לטפל בכמה מקורות נתונים בו-זמנית. הצג יאזין לערוצים של שירותים שונים במקביל ויגיב בהתאם כאשר יגיעו נתונים או כשלא יהיו עדכונים זמינים.

תארגנו את הקוד בשני קבצים:

  • monitor.go: הגדירו את הלוגיקה לניטור השירות באמצעות טיפול בערוצים המבוסס על select.

    צרו מבנה ServiceUpdate עם השדות Name (string) ו-Status (string).

    ממשו פונקציה Monitor שמקבלת שלושה ערוצים: primary chan ServiceUpdate, backup chan ServiceUpdate ו-done chan bool. הפונקציה צריכה להשתמש ב-select כדי להאזין לכל שלושת הערוצים בו-זמנית:

    • בעת קבלת נתונים מ-primary: החזירו את המחרוזת המעוצבת Primary: [Name] is [Status]
    • בעת קבלת נתונים מ-backup: החזירו את המחרוזת המעוצבת Backup: [Name] is [Status]
    • בעת קבלת נתונים מ-done: החזירו Monitoring stopped

    ממשו פונקציה CheckStatus שמקבלת ערוץ יחיד updates chan ServiceUpdate ומשתמשת ב-select עם מקרה default כדי לבצע בדיקה לא חוסמת:

    • אם יש עדכון זמין: החזירו Update available: [Name]
    • אם אין עדכון זמין (מקרה ברירת המחדל): החזירו No updates pending
  • main.go: הגדירו את הערוצים והדגימו התנהגות של select חוסם ולא חוסם.

    קראו מהקלט מצב שקובע איזה תרחיש להפעיל: primary, backup, stop או check.

    במצבים primary, backup ו-stop: קראו גם שם שירות וסטטוס. צרו את שלושת הערוצים, הפעילו גורוטינה ששולחת נתונים לערוץ המתאים בהתאם למצב, לאחר מכן קראו ל-Monitor והדפיסו את התוצאה.

    במצב check: קראו שם שירות וסטטוס, וגם דגל (send או nosend) שמציין אם לשלוח נתונים לפני הבדיקה. צרו ערוץ מאוחסן בקיבולת 1. אם הדגל הוא send, שלחו עדכון לערוץ לפני הקריאה ל-CheckStatus. הדפיסו את התוצאה של CheckStatus.

הקלט הבא יסופק:

  • שורה 1: מצב (primary, backup, stop או check)
  • שורה 2: שם השירות
  • שורה 3: סטטוס השירות
  • שורה 4 (רק במצב check): send או nosend

לדוגמה, עבור הקלט:

primary
Database
healthy

הפלט שלכם צריך להיות:

Primary: Database is healthy

ועבור הקלט:

check
Cache
active
nosend

הפלט שלכם צריך להיות:

No updates pending

נסו בעצמכם

package main

import (
	"fmt"
)

func main() {
	var mode string
	fmt.Scanln(&mode)

	var serviceName string
	fmt.Scanln(&serviceName)

	var serviceStatus string
	fmt.Scanln(&serviceStatus)

	// TODO: טפל במצבים השונים: "primary", "backup", "stop", "check"
	
	// עבור המצבים "primary", "backup", "stop":
	// - צור שלושה ערוצים: primary, backup, done
	// - הפעל גורוטינה ששולחת נתונים לערוץ המתאים
	// - קרא ל-Monitor והדפס את התוצאה
	
	// עבור המצב "check":
	// - קרא את הדגל הנוסף ("send" או "nosend")
	// - צור ערוץ עם מאגר בקיבולת 1
	// - אם הדגל הוא "send", שלח עדכון לערוץ
	// - קרא ל-CheckStatus והדפס את התוצאה
	
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין