Channel дэвшилтэт хэрэглээ
Өмнөх хичээлд unbuffered channel-ийн үндсийг сурсан. Одоо илүү хүчирхэг хэв маягуудыг судална — buffered channel, fan-out, worker pool. Эдгээр нь бодит backend системд байнга ашиглагддаг хэв маягууд юм.
Buffered Channel
Энгийн (unbuffered) channel-д илгээгч хүлээн авагчийг хүлээдэг. Buffered channel нь тодорхой хэмжээний утгыг хуримтлуулж чаддаг тул илгээгч нэн даруй хаагдахгүй:
package main
import "fmt"
func main() {
// 3 утга багтах buffered channel
ch := make(chan string, 3)
// Горутингүйгээр шууд илгээх боломжтой
ch <- "нэг"
ch <- "хоёр"
ch <- "гурав"
fmt.Println(<-ch) // нэг
fmt.Println(<-ch) // хоёр
fmt.Println(<-ch) // гурав
}
Buffer дүүрсэн үед илгээгч дахин хүлээнэ. Buffer хоосон үед хүлээн авагч хүлээнэ.
Fan-out хэв маяг
Нэг channel-аас олон горутин утга авах — ажлыг хуваарилах хамгийн энгийн арга:
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Ажилтан %d: ажил %d-г гүйцэтгэж байна\n", id, job)
}
}
func main() {
jobs := make(chan int, 10)
var wg sync.WaitGroup
// 3 ажилтан горутин ажиллуулах
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, jobs, &wg)
}
// 9 ажил илгээх
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
fmt.Println("Бүх ажил дууслаа")
}
jobs channel-аас гурван ажилтан өрсөлдөн утга авна. Runtime хэн нь түрүүлж чөлөөлөгдсөний дагуу хуваарилна.
Fan-in хэв маяг
Олон channel-ийн өгөгдлийг нэгт нийлүүлэх:
package main
import (
"fmt"
"sync"
)
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func merge(channels ...<-chan int) <-chan int {
merged := make(chan int)
var wg sync.WaitGroup
forward := func(ch <-chan int) {
defer wg.Done()
for v := range ch {
merged <- v
}
}
wg.Add(len(channels))
for _, ch := range channels {
go forward(ch)
}
go func() {
wg.Wait()
close(merged)
}()
return merged
}
func main() {
ch1 := generate(1, 3, 5)
ch2 := generate(2, 4, 6)
for v := range merge(ch1, ch2) {
fmt.Println(v)
}
}
Pipeline хэв маяг
Нэг горутиний гаралт нь нөгөөний оролт болдог дамжуулах хоолой:
package main
import "fmt"
func double(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for v := range in {
out <- v * 2
}
close(out)
}()
return out
}
func addTen(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for v := range in {
out <- v + 10
}
close(out)
}()
return out
}
func main() {
// Эх өгөгдөл
source := make(chan int)
go func() {
for _, v := range []int{1, 2, 3, 4, 5} {
source <- v
}
close(source)
}()
// Pipeline: source → double → addTen
result := addTen(double(source))
for v := range result {
fmt.Println(v) // (1*2)+10=12, (2*2)+10=14, ...
}
}
Pipeline нь Unix-ийн | оператортай адил — нэг процессын гаралт нөгөөний оролт болно.
Дараагийн хичээлд:
Олон channel-ийг нэгэн зэрэг сонсох select хуваарилагчийг судална. select нь Go-ийн concurrency-ийн маш хүчирхэг хэрэгсэл юм.