Kanalni faqat yuborish yoki faqat qabul qilish uchun cheklash
Oddiy kanal (chan int) orqali ham yuborish, ham qabul qilish mumkin. Lekin funksiya faqat bittasini bajarishi kerak bo'lsa, bu niyatni kanal yo'nalishi (channel direction) orqali kompilyator darajasida aniq belgilash mumkin — bu xatolarni erta, kompilyatsiya bosqichidayoq ushlab qolishga yordam beradi.
func send(ch chan<- int, val int) { // faqat YUBORISH uchun kanal
ch <- val
}
func receive(ch <-chan int) int { // faqat QABUL QILISH uchun kanal
return <-ch
}
chan<- int — bu kanaldan faqat yuborish mumkin.<-chan int — bu kanaldan faqat qabul qilish mumkin.package main
import "fmt"
func send(ch chan<- int, val int) {
ch <- val
}
func receive(ch <-chan int) int {
return <-ch
}
func main() {
ch := make(chan int, 1)
send(ch, 42)
fmt.Println(receive(ch))
}
Natija:
42
Oddiy ikki yo'nalishli kanal (chan int) avtomatik ravishda kerakli bir yo'nalishli turga aylanadi — send() chaqirilganda Go kompilyatori uni chan<- int sifatida qabul qiladi. Lekin teskarisi ishlamaydi: agar receive() ichida ch <- 5 kabi yuborishga urinsangiz, kompilyator xatolik beradi — bu esa dasturchining niyatini aniq va xavfsiz qiladi.
[!NOTE] Bu pattern, ayniqsa, katta dasturlarda "kim bu kanalga yozadi, kim o'qiydi" degan javobgarlikni funksiya imzosining o'zidan bilib olish imkonini beradi — kodni o'qigan boshqa dasturchi hujjatga qaramasdan tushunadi.
close()Agar yuboruvchi tomon "boshqa hech qanday ma'lumot yubormayman" deb bildirmoqchi bo'lsa, close() funksiyasi ishlatiladi:
package main
import "fmt"
func producer(ch chan<- int) {
for i := 1; i <= 5; i++ {
ch <- i
}
close(ch) // barcha ma'lumot yuborilgach, kanalni yopamiz
}
func main() {
ch := make(chan int)
go producer(ch)
// range kanal yopilishi bilan avtomatik to'xtaydi
for v := range ch {
fmt.Println(v)
}
fmt.Println("Kanal yopilgach sikl to'xtadi")
}
Natija:
1
2
3
4
5
Kanal yopilgach sikl to'xtadi
for v := range ch — kanal yopilmaguncha undan qiymatlarni birin-ketin qabul qilib turadi, kanal yopilgan zahoti sikl o'z-o'zidan to'xtaydi (break yozishning hojati yo'q).
v, ok := <-chmapdagi "comma-ok" idiomasiga o'xshab, kanaldan qiymat olishda ham kanal yopilgan-yopilmaganini bilish mumkin:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
close(ch)
v1, ok1 := <-ch // hali yopilishdan oldin yuborilgan qiymat bor
fmt.Println(v1, ok1)
v2, ok2 := <-ch // kanal bo'sh VA yopilgan
fmt.Println(v2, ok2)
}
Natija:
10 true
0 false
[!IMPORTANT] Yopilgan kanaldan qiymat olish xato emas — agar buferda hali yuborilgan qiymatlar qolgan bo'lsa, ular baribir olinaveradi (
ok = true), faqat bufer butunlay bo'shagandan keyinginaok = falseva nol qiymat (zero value) qaytadi. Bu — Goning kanallar orqali "ish tugadi" signalini xavfsiz uzatishga imkon beruvchi asosiy mexanizmi.
ch := make(chan int, 1)
close(ch)
ch <- 1 // PANIC: send on closed channel
ch := make(chan int)
close(ch)
close(ch) // PANIC: close of closed channel
[!WARNING]
- Yopilgan kanalga yuborish darhol
panicberadi.- Bitta kanalni ikki marta yopish ham
panicberadi.- Kanalni har doim faqat yuboruvchi tomon yopishi kerak, hech qachon qabul qiluvchi tomon emas — chunki qabul qiluvchi kanalga yana qancha ma'lumot kelishini bilmaydi.
| Vazifa | Kod |
|---|---|
| Faqat yuborish uchun kanal parametri | func f(ch chan<- Tur) |
| Faqat qabul qilish uchun kanal parametri | func f(ch <-chan Tur) |
| Kanalni yopish ("boshqa ma'lumot yo'q" signali) | close(ch) |
| Kanal yopilgan-yopilmaganini tekshirish | v, ok := <-ch |
| Kanal yopilguncha barcha qiymatlarni olish | for v := range ch { ... } |