@nigun הקוד שהבאתי הוא בסיס מופשט להבנת הענין
package main
import (
"fmt"
"time"
)
var (
// הערוץ הראשי שאליו מוזרם המידע
mainChan = make(chan string)
// מערך של ערוצי משנה
// כל פונקציה שתרצה לקבל מידע מהערוץ הראשי תצטרך ליצור ערוץ משנה ולהוסיף אותו למערך הזה
channels = []chan string{}
)
func main() {
go func() {
// פונקציה היחידה שמקבל את המידע מהערוץ הראשי ומזרימה אותו לערוצי המשנה
msg := <-mainChan
for _, c := range channels {
c <- msg
}
}()
go func(c chan string) {
time.Sleep(2 * time.Second)
c <- "foo"
}(mainChan)
go someThread()
go someThread()
go someThread()
go someThread()
time.Sleep(4 * time.Second)
}
func someThread() {
c := addListener()
s := <-c
fmt.Println(s)
}
func addListener() chan string {
// הפונקציה שתפקידה ליצור את ערוצי המשנה ולהוסיף אותם למערך כדי להאזין למידע מהערוץ הראשי
newChan := make(chan string)
channels = append(channels, newChan)
return newChan
}
זה עובד טוב בשביל פונקציות חד פעמיות, אם תרצה להפוך את זה למאזינים רב פעמיים, לא צריך לעשות הרבה, רק להוסיף לולאות אינסופיות שמאזינות למידע מהערוץ, כך שברגע שיתקבל מידע מהערוץ הפונקציה תעשה את המוטל עליה ומיד אחר כך תמשיך לאיטרציה הבאה בלולאה ששוב מאזינה לערוץ:
package main
import (
"fmt"
"time"
)
var (
mainChan = make(chan string)
channels = []chan string{}
)
func main() {
go func() {
for { // << לולאה אינסופית שמאזינה לערוץ הראשי
msg := <-mainChan
for _, c := range channels {
c <- msg
}
}
}()
go func(c chan string) {
time.Sleep(1 * time.Second)
c <- "foo"
time.Sleep(1 * time.Second)
c <- "foo" // << שליחת מידע לערוץ הראשי פעם נוספת
}(mainChan)
go someThread()
go someThread()
go someThread()
go someThread()
time.Sleep(4 * time.Second)
}
func someThread() {
c := addListener()
for { // << לולאה אינסופית שמאזינה לערוץ המשנה
s := <-c
fmt.Println(s)
}
}
func addListener() chan string {
newChan := make(chan string)
channels = append(channels, newChan)
return newChan
}
עד כאן הכל פשוט, הבעיה מתחילה בסיטואציה של השואל בסטאק, שיתכנו מאזינים שנרשמו לערוצי המשנה, ובהמשך הם יתנתקו וירדו מההאזנה, הבעיה היא שכל מאזין מריץ לולאה אינסופית, ואם לא תעצור את הלולאה כשהוא יתנתק זה יגרום לשימוש מיותר בזיכרון. בשביל זה הוא צריך להוסיף לכל מאזין גם תנאי עצירה, ולכן הוא הוסיף את הערוץ quit, וזה סיבך את הקוד.
האם אתה גם צריך את זה? זה תלוי באופי השימוש של המאזינים שאתה מקים, אם הם אמורים לרוץ כל חיי האפליקציה, אז זה מיותר, אם יש סיכוי שחלק מהם או כולם יגמרו את התפקיד שלהם וימותו, אז תצטרך לדאוג לעצירת הלולאה.
גם למקרה שצריך לעצור את הלולאה, אם המקרה של סיום התפקיד של הפונקציה ידוע וצפוי מראש, לא תצטרך להשתמש בשיטה של הערוץ quit, אלא פשוט תעצור את הלולאה בעצמך, אם זה לא תלוי בידך אלא זה מקרה לא צפוי (כמו במקרה של השואל שם שמשתמש יתנתק) לזה נועד השימוש בערוץ וב select
בעיה נוספת שהעלו שם, אם המאזינים נרשמים ומתווספים למערך בו בזמן שהערוץ בפעולה, והפתרון שהוא הציע זה לנעול את הערוץ עם sync.Mutex
בזמן שמוסיפים מאזין ולשחרר אותו אחר כך, אבל לא הצלחתי להבין מה בדיוק יגרום לבעיה ולמה צריך את הפיתרון.