Skip to content

select operatori

Bir nechta channellar bilan bir vaqtda ishlash talab etilganda select operatori yordamga keladi. Bu operator switch ga o'xshash tarzda ishlaydi, ammo farqli jihati shundaki, select bir nechta channelni bir vaqtning o'zida nazorat qiladi va ulardan tayyor bo'lgan birinchisini bajaradi. Bu gorutinlar (ya'ni, parallel bajariladigan vazifalar) bilan ishlaganda juda qulay.


asosiy sintaksis

1
2
3
4
5
6
7
8
select {
    case msg1 := <-ch1:
        fmt.Println("ch1 channelidan xabar oldi:", msg1)
    case ch2 <- msg2:
        fmt.Println("ch2 channeliga xabar yuborildi:", msg2)
    default:
        fmt.Println("Hech qanday channel tayyor emas")
}
  • Har bir case qatori, biror channel bilan o'qish yoki yozishni bildiradi.
  • default bloki hech bir channel tayyor bo'lmaganda bajariladi.
  • select blokida faqatgina bitta case bajariladi. Birinchi bo'lib tayyor bo'lgan channel uchun ishga tushadi.

Misol:

package main

import (
    "fmt"
    "time"
)

func main() {
    chan1 := make(chan string)
    chan2 := make(chan string)

    go func(){
        time.Sleep(2 * time.Second)
        chan1 <- "Assalomu alaykum 1!"
    }()

    go func(){
        time.Sleep(3 * time.Second)
        chan2 <- "Assalomu alaykum 2!"
    }()

    select {
    case rec1 := <-chan1:
        fmt.Println(rec1)
    case rec2 := <-chan2:
        fmt.Println(rec2)
    }
}

Bu yerda ikkita unbuffered channel yaratilgan:

  • chan1 — 2 soniyadan keyin xabar oladi
  • chan2 — 3 soniyadan keyin xabar oladi

Gorutinlar birinchisida 2 soniya kutiladi so'ng chan1 kanaliga "Assalomu alaykum 1!" yuboriladi, ikkinchisida esa 3 soniya kutiladi so'ng chan2 kanaliga "Assalomu alaykum 2!" yuboriladi.

select operatori bir vaqtning o'zida har ikki kanalni kuzatadi. Qaysi kanal birinchi bo'lib tayyor bo'lsa, o'sha case bajariladi, faqat bitta case ishlaydi. Boshqa case'lar ishlamaydi hech bir kanal tayyor bo'lmasa, kutishda qoladi(bloklanadi).

select birinchi bo'lib tayyor bo'lgan kanalni tanlaydi, shu sababli doim chan1 dan kelgan xabar ekranga chiqadi, chunki u 2 soniyada tayyor bo'ladi chan2 esa 3 soniyada.

Bloklanish nima degani?

Go tilida kanal orqali ma'lumot yuborish yoki qabul qilish bloklanadi — ya'ni kanalda ikkinchi tomon tayyor bo'lmaguncha kutadi, va shu paytda gorutinning ishlashi to'xtab qoladi.

Agar ikkala channelda ma'lumot olmoqchi bo'lsak nima qilishimiz kerak? Quyidagi misolda ko'ramiz.

Agar siz selectni for ichiga olib, kanal to'liq tugamaguncha kutadigan qilsangiz, ikkala kanal ham ishlaydi.

Misol:

package main

import (
    "fmt"
    "time"
)

func main() {
    chan1 := make(chan string)
    chan2 := make(chan string)

    go func(){
        time.Sleep(2 * time.Second)
        chan1 <- "Assalomu alaykum 1!"
    }()

    go func(){
        time.Sleep(3 * time.Second)
        chan2 <- "Assalomu alaykum 2!"
    }()

    for i := 0; i < 2; i++ {
        select {
        case rec1 := <-chan1:
            fmt.Println(rec1)
        case rec2 := <-chan2:
            fmt.Println(rec2)
        }
    }

}

Bu holatda:

  • Birinchi select 2-soniyada chan1ni o'qiydi
  • Ikkinchi select 3-soniyada chan2ni o'qiydi

Misol:

ch := make(chan int)
ch <- 10 // ❗️Bu yerda bloklanadi!

Bu yerda ch <- 10 yozilganda bu kanalni o'qishga tayyor bo'lmasa, gorutin kutib qoladi — bu bloklanish deyiladi.

Bloklanmaslik nima?

Agar siz kanalni tekshirib, ma'lumot tayyor bo'lmasa kutmasdan davom etsangiz, bu bloklanmasdan ishlash deb ataladi.

Misol:

1
2
3
4
5
6
select {
    case val := <-ch:
        fmt.Println("Qabul qilindi:", val)
    default:
        fmt.Println("Kanal bo'sh, davom etamiz")
}

Bu yerda:

  • Agar ch kanalida ma'lumot bo'lsa, u olinadi va case bajariladi.
  • Agar yo'q bo'lsa, default bajariladi — kutmasdan davom etiladi.

Tasavvur qiling:

  • Siz do'stingizga telefon qilyapsiz (ch <- xabar)
  • Agar u javob bermasa, siz kutib turasiz — bu bloklanish.
  • Agar siz: "Agar u 5 soniya ichida javob bermasa, boshqa ish qilaman" desangiz — bu bloklanmasdan davom etish.

Case larsiz cheksiz bloklanish (Infinite Blocking without Cases)

Agar select operatori ichida hech qanday case yozilmagan bo'lsa, u cheksiz bloklanadi ya'ni, cheksiz kutishda qoladi. Bu ko'pincha dasturda cheksiz kutish kerak bo'lgan holatlarda ishlatiladi.

1
2
3
4
5
package main

func main() {
    select {}  // Bu cheksiz bloklanadi, chunki hech qanday case yo'q
}

Bu usuldan goroutinelar fon rejimda ishlashda davom etishi, lekin main funksiyasi tugamasligi kerak bo'lgan holatlarda foydalaniladi.