40 / 68

Buferlangan kanallar

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:

example.go
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.


1. Buferga sig'guncha bloklanmaydi

example.go
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:

kod
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.


2. Hozirgi to'lganlik va umumiy sig'im: len() va cap()

example.go
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:

kod
2
3

3. Bufer to'lib ketsa nima bo'ladi?

Buferlangan kanal ham cheksiz emas — agar bufer to'lib, hali hech kim qabul qilmagan bo'lsa, keyingi yuborish amali baribir bloklanadi:

example.go
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:

kod
Birinchi qiymat yuborildi
Hozircha faqat bitta joy bor edi...
Qabul qilindi: 1
Ikkinchi qiymat yuborildi
Qabul qilindi: 2

4. Qachon buferSIZ, qachon buferlangan kanal ishlatiladi?

HolatTavsiya
Ikki goroutine bir-birini "qo'lma-qo'l" kutib, aniq sinxronlashishi kerakBuferSIZ kanal
Yuboruvchi qabul qiluvchidan tezroq ishlab, ma'lum miqdorda "old zaxira" kerakBuferlangan kanal
Bir nechta ishchi (worker) natijalarini yig'ish, ammo ularning barchasi bir vaqtda tayyor turishini talab qilmaslikBuferlangan 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.


Xulosa: qachon qaysi funksiya kerak?

VazifaKod
BuferSIZ kanal yaratishmake(chan Tur)
Buferlangan kanal yaratishmake(chan Tur, sigim)
Hozirgi to'lganlikni bilishlen(ch)
Umumiy sig'imni bilishcap(ch)