Oddiy (buferSIZ) va buferlangan kanal farqi
Oldingi darsda ko'rgan kanallarning barchasi — buferSIZ (unbuffered) kanallar edi. Ularda yuborish amali (ch <- data) boshqa bir goroutine aynan o'sha paytda qabul qilishga (<-ch) tayyor bo'lmaguncha bloklanadi (kutadi).
Goda shuningdek buferlangan (buffered) kanal ham yaratish mumkin — u ma'lum miqdordagi qiymatlarni "ombor" kabi vaqtincha o'zida saqlab turadi, hech kim darhol qabul qilmasa ham:
ch := make(chan int, 3) // 3 ta sig'imga ega buferlangan kanal
Ikkinchi parametr — sig'im (capacity) — kanal qabul qiluvchi tomon kutmasdan turib nechta qiymatni "ombor"ida saqlashi mumkinligini bildiradi.
package main
import "fmt"
func main() {
ch := make(chan int, 3)
// uchtasi ham darhol "ketadi" — hech kim ularni hali qabul qilmagan bo'lsa ham
ch <- 1
ch <- 2
ch <- 3
fmt.Println("Uchtasi ham muammosiz yuborildi (buferga sig'di)")
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Natija:
Uchtasi ham muammosiz yuborildi (buferga sig'di)
1
2
3
Agar bu ch buferSIZ (make(chan int)) bo'lganida, birinchi ch <- 1 qatorining o'zida dastur "qotib qolar" edi — chunki hech kim hali qabul qilishga tayyor emas.
len() va cap()package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
fmt.Println(len(ch)) // hozir kanalda nechta element bor
fmt.Println(cap(ch)) // kanalning umumiy sig'imi
}
Natija:
2
3
Buferlangan kanal ham cheksiz emas — agar bufer to'lib, hali hech kim qabul qilmagan bo'lsa, keyingi yuborish amali baribir bloklanadi:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan int, 1) // sig'imi bor-yo'g'i 1
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
ch <- 1 // buferga sig'adi, darhol o'tadi
fmt.Println("Birinchi qiymat yuborildi")
ch <- 2 // bufer band, qabul qilinmaguncha shu yerda kutadi
fmt.Println("Ikkinchi qiymat yuborildi")
}()
time.Sleep(500 * time.Millisecond)
fmt.Println("Hozircha faqat bitta joy bor edi...")
fmt.Println("Qabul qilindi:", <-ch) // endi bufer bo'shadi, ikkinchi yuborish davom etadi
fmt.Println("Qabul qilindi:", <-ch)
wg.Wait()
}
Natija:
Birinchi qiymat yuborildi
Hozircha faqat bitta joy bor edi...
Qabul qilindi: 1
Ikkinchi qiymat yuborildi
Qabul qilindi: 2
| Holat | Tavsiya |
|---|---|
| Ikki goroutine bir-birini "qo'lma-qo'l" kutib, aniq sinxronlashishi kerak | BuferSIZ kanal |
| Yuboruvchi qabul qiluvchidan tezroq ishlab, ma'lum miqdorda "old zaxira" kerak | Buferlangan kanal |
| Bir nechta ishchi (worker) natijalarini yig'ish, ammo ularning barchasi bir vaqtda tayyor turishini talab qilmaslik | Buferlangan kanal (sig'imi worker soniga teng) |
[!IMPORTANT] Buferlangan kanal sinxronizatsiya muammosini butunlay yo'q qilmaydi — u faqat qanchalik "erkinlik" (necha qadam oldinda ishlash mumkinligi) berishni belgilaydi. Agar buferning sig'imi juda katta tanlansa, dasturdagi haqiqiy nosozliklar (masalan, hech kim qabul qilmayotgani) uzoq vaqt "sezilmasdan" yashirinib qolishi mumkin. Aksariyat hollarda, ayniqsa o'rganish bosqichida, buferSIZ kanaldan boshlab, faqat aniq sabab bo'lgandagina buferlangan kanalga o'tish tavsiya etiladi.
| Vazifa | Kod |
|---|---|
| BuferSIZ kanal yaratish | make(chan Tur) |
| Buferlangan kanal yaratish | make(chan Tur, sigim) |
| Hozirgi to'lganlikni bilish | len(ch) |
| Umumiy sig'imni bilish | cap(ch) |