select хуваарилагч
Нэг горутин олон channel-ийг нэгэн зэрэг сонсох шаардлага байнга гардаг. Жишээ нь: өгөгдөл хүлээж байхдаа timeout-ийг ч шалгах хэрэгтэй. select нь яг энэ зорилгоор бий болсон.
select үндэс
select нь switch-тэй төстэй боловч channel үйлдлүүдэд ажилладаг. Бэлэн болсон channel-ийн case-ийг гүйцэтгэнэ:
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "ch1-ээс мессеж"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "ch2-ээс мессеж"
}()
// Хоёр channel-ийг нэгэн зэрэг сонсох
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("ch2:", msg2)
}
}
}
select нь бэлэн болсон case руу шилждэг. Хоёр case нэгэн зэрэг бэлэн болвол санамсаргүйгээр нэгийг сонгоно.
Timeout хэрэгжүүлэх
time.After ашиглан хугацаа хэтэрсэн тохиолдлыг зохицуулах:
package main
import (
"fmt"
"time"
)
func fetchData(ch chan<- string) {
// Удаан ажиллах процессийг дуурайж байна
time.Sleep(2 * time.Second)
ch <- "сервераас ирсэн өгөгдөл"
}
func main() {
ch := make(chan string)
go fetchData(ch)
select {
case data := <-ch:
fmt.Println("Хүлээн авсан:", data)
case <-time.After(1 * time.Second):
fmt.Println("Хугацаа хэтэрлээ — timeout!")
}
}
time.After(1 * time.Second) нь 1 секундын дараа утга илгээх channel буцаадаг. fetchData 2 секунд болдог тул timeout case ялна.
default case — блоклохгүй шалгалт
default case нь бусад channel бэлэн болоогүй үед нэн даруй ажилладаг:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
// Channel дүүрэн үү шалгах
select {
case ch <- 10:
fmt.Println("Утга илгээлээ")
default:
fmt.Println("Channel дүүрэн байна, илгээж чадсангүй")
}
// Хүлээн авах, хоосон үед skip хийх
select {
case v := <-ch:
fmt.Println("Утга авлаа:", v)
default:
fmt.Println("Channel хоосон байна")
}
select {
case v := <-ch:
fmt.Println("Утга авлаа:", v)
default:
fmt.Println("Channel хоосон байна")
}
}
default байгаа тул select хэзээ ч хаагдахгүй — бэлэн channel байхгүй бол default ажиллана.
Горутин зогсоох — done channel
Горутиныг гадаасаа зогсоох нийтлэг хэв маяг:
package main
import (
"fmt"
"time"
)
func worker(done <-chan struct{}) {
for {
select {
case <-done:
fmt.Println("Ажилтан зогслоо")
return
default:
fmt.Println("Ажилтан ажиллаж байна...")
time.Sleep(300 * time.Millisecond)
}
}
}
func main() {
done := make(chan struct{})
go worker(done)
time.Sleep(1 * time.Second)
close(done) // горутиныг зогсооно
time.Sleep(100 * time.Millisecond)
fmt.Println("Дууслаа")
}
chan struct{} нь утга дамжуулах шаардлагагүй, зөвхөн дохио илгээхэд ашигладаг хамгийн хямд channel юм. close(done) нь бүх хүлээн авагчид нэгэн зэрэг дохио илгээдэг.
select дотор олон үйлдэл
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(200 * time.Millisecond)
done := make(chan bool)
go func() {
time.Sleep(1 * time.Second)
done <- true
}()
count := 0
for {
select {
case <-done:
fmt.Printf("Нийт %d удаа тиклэв\n", count)
ticker.Stop()
return
case t := <-ticker.C:
count++
fmt.Println("Тик:", t.Format("15:04:05.000"))
}
}
}
time.NewTicker нь тогтмол хугацааны зайтай дохио илгээдэг channel юм. ticker.Stop() дуусгахдаа дуудна.
Дараагийн хичээлд:
Горутинүүдийг аюулгүйгээр нэгтгэх sync.WaitGroup болон хуваалцсан өгөгдлийг хамгаалах sync.Mutex-ийг судална.