Зэрэгцээ ажиллаж байгаа хоёр go функц хоорондоо мэдээлэл солилцохдоо суваг ашиглана. Дараах програмд суваг ашигласан байна:
package main
import (
"fmt"
"time"
)
func pinger(c chan string) {
for i := 0; ; i++ {
c <- "ping"
}
}
func printer(c chan string) {
for {
msg := <- c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string = make(chan string)
go pinger(c)
go printer(c)
var input string
fmt.Scanln(&input)
}
Энэ програм “ping” текстийг тасралтгүй хэвлэх болно, зогсоохын тулд Enter товчлуур дарах хэрэгтэй.
Суваг үүсгэхдээ chan
түлхүүр үгийн араас уг сувгаар дамжих өгөгдлийн төрлийг зааж бичнэ. Зүүн сум <-
үйлдэл нь сувгаар өгөгдөл илгээх, сувгаас өгөгдөл хүлээн авахад ашиглагдана. c <- "ping"
заавар нь "ping" текстийг c
сувгаар илгээ гэсэн утгатай юм. msg := <- c
заавар нь c
сувгаас мессеж уншиж msg
хувьсагчид хадгал гэсэн утгатай.
Go функцүүдийн хооронд суваг ашигласнаар тэдгээрийн ажиллагааг харилцан дараалалд оруулдаг. Тухайлбал pinger()
функц сувгаар мессеж илгээх бөгөөд түүнийг printer()
функц хүлээн авах хүртэл уг цэг дээр ажиллагаа нь зогсох болно. Үүнийг мөн өөрөөр түгжээ гэж хэлдэг.
Өмнөх сувгаар дахиад нэг өгөгдөл илгээдэг go функц нэмж юу болохыг харая:
func ponger(c chan string) {
for i := 0; ; i++ {
c <- "pong"
}
}
main функцийг дараах байдлаар өөрчлөнө:
func main() {
var c chan string = make(chan string)
go pinger(c)
go ponger(c)
go printer(c)
var input string
fmt.Scanln(&input)
}
Дээрх функцийг нэмсэнээр програм “ping” болон “pong” текстүүдийг тасралтгүй хэвлэх болно.
Сувагт чиглэл зааж болно, ингэснээр зөвхөн хүлээж авах эсвэл илгээх чиглэлтэй болно. Жишээлбэл pinger()
функцийг дараах байдлаар зөвхөн илгээгч болгон тодорхойлж болно:
func pinger(c chan<- string)
Үүний үр дүнд c
суваг уруу зөвхөн өгөгдөл илгээж болно, энэ сувгаас өгөгдөл унших гэж оролдвол алдаа болох болно.
Үүнтэй төстэйгээр printer()
функцийг зөвхөн хүлээн авагч болгон өөрчилж болно:
func printer(c <-chan string)
Go хэлэнд сувагтай ажиллахад зориулсан select
түлхүүр үг байдаг. Энэ функц нь switch
заавартай төстэй ажилладаг. Өөрөөр хэлбэл олон сувгаас дохио ирсэн сувгийг сонгож хариу хүлээн авах, боловсруулах зориулалттай заавар юм.
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for {
c1 <- "from 1"
time.Sleep(time.Second * 2)
}
}()
go func() {
for {
c2 <- "from 2"
time.Sleep(time.Second * 3)
}
}()
go func() {
for {
select {
case msg1 := <- c1:
fmt.Println(msg1)
case msg2 := <- c2:
fmt.Println(msg2)
}
}
}()
var input string
fmt.Scanln(&input)
}
Дээрх програм нь 2 секунд тутамд “from 1” текст, 3 секунд тутамд “from 2” текст хэвлэнэ.
select
нь өгөгдөл хүлээн авах боломжтой байгаа сувгийг сонгоно (эсвэл илгээх боломжтой). Хэрэв нэгээс олон сувгаас өгөгдөл унших боломжтой болсон байвал санамсаргүйгээр нэгийг нь сонгоно. Хэрэв ямар ч суваг бэлэн биш байвал бэлэн болтол хүлээнэ.
select
зааврыг мөн ихэвчлэн хугацаа хэтрэлт (timeout) тооцоолоход ашигладаг:
select {
case msg1 := <- c1:
fmt.Println("Мессеж 1", msg1)
case msg2 := <- c2:
fmt.Println("Мессеж 2", msg2)
case <- time.After(time.Second):
fmt.Println("timeout")
}
time.After
нь өгөгдсөн тодорхой хугацааны дараа мессеж илгээх шинэ суваг үүсгэдэг. Цаг болоход энэ сувгаар дохио ирнэ, энэ дохио нь одоогийн цагийн мэдээлэл байна. Дээрх жишээнд цагийн мэдээлэл нь чухал биш учраас түүнийг хувьсагчид хадгалаагүй байна.
Мөн select
зааварт default
тохиолдол нэмж болно. default
нь бүх суваг бэлэн бус буюу дохио ирэхгүй байх тохиолдол юм.
select {
case msg1 := <- c1:
fmt.Println("Мессеж 1", msg1)
case msg2 := <- c2:
fmt.Println("Мессеж 2", msg2)
case <- time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("өгөгдөл бэлэн суваг алга!")
}
Суваг үүсгэх үед сувгийн багтаамжийг заасан хоёр дахь параметр заах боломжтой:
c := make(chan int, 1)
Дээрх заавар нь 1
багтаамжтай суваг үүсгэнэ.
Ердийн суваг нь синхрон байдаг, ө.х сувгийн хоёр талд байгаа илгээгч, хүлээн авагч нь нөгөөгөө бэлэн болтол хүлээдэг. Харин буферт суваг нь асинхрон байдаг; ө.х суваг дүүрэхгүй л бол илгээгч, хүлээн авагчид дээр хүлээлт үүсэхгүй гэсэн үг.