Bufersiz, buferli va yo‘nalishli channellar
Go’da channel bir xil qiymat uzatish modeliga ega, ammo uning sig‘imi va ruxsat etilgan yo‘nalishi turlicha bo‘lishi mumkin. Bufersiz channel yuboruvchi bilan qabul qiluvchini bevosita uchrashtiradi. Buferli channel ular orasiga cheklangan navbat qo‘yadi. Yo‘nalishli channel esa funksiya qaysi amalni bajarishi mumkinligini tur darajasida cheklaydi.
Bu farqlar faqat sintaksis emas. Ular bloklanish, yuklamani boshqarish va API dizayniga ta’sir qiladi.
Bufersiz channel
Sig‘im ko‘rsatilmasa yoki 0 berilsa, bufersiz channel yaratiladi:
Bufersiz channel qiymatni saqlash uchun navbatga ega emas. Send mos receive tayyor bo‘lguncha, receive esa mos send tayyor bo‘lguncha bloklanadi. Qiymat almashilganda ikki goroutine sinxronlashadi.
Misol: ish tugaganini signal qilish
| main.go | |
|---|---|
Natija:
struct{}{} foydali ma’lumot saqlamaydigan signal qiymati. main receive amalida prepare signal yuborguncha kutadi. Senddan oldingi Println bajarilib bo‘lgach signal yuboriladi, shu sabab yakuniy satr keyin chiqadi.
Bufersiz channel ishni topshirish, javobni kutish yoki ikki goroutine o‘rtasida aniq sinxronizatsiya nuqtasi yaratish uchun mos.
Qabul qiluvchisiz send
Bu dastur deadlock bilan tugaydi. main qiymatni qabul qiladigan goroutine bo‘lmagani uchun sendda bloklanadi.
Buferli channel
Musbat sig‘im berilsa, buferli channel yaratiladi:
Bu channel uchta qiymatni navbatda saqlay oladi:
- buferda bo‘sh joy bo‘lsa, send qabul qiluvchini kutmasdan tugashi mumkin;
- bufer to‘lsa, keyingi send joy bo‘shaguncha bloklanadi;
- buferda qiymat bo‘lsa, receive uni darhol olishi mumkin;
- bufer bo‘sh bo‘lsa, receive yangi qiymat kelguncha bloklanadi.
Eng kichik misol
| main.go | |
|---|---|
Natija:
Ikki send qabul qiluvchisiz tugaydi, chunki bufer sig‘imi 2. Channel qiymatlarni yuborilgan tartibda saqlaydi. Bitta channel ichidagi qiymatlar FIFO tartibida qabul qilinadi.
FIFO turli yuboruvchi goroutinelarning qaysi biri birinchi send qilishini kafolatlamaydi. Scheduler sabab sendlar channelga boshqa tartibda yetib kelishi mumkin. Channel faqat qabul qilish tartibi muvaffaqiyatli sendlar tartibiga mos bo‘lishini ta’minlaydi.
Uchinchi qiymatni qabul qilishdan oldin yuborishga urinish mainni bloklab qo‘yardi:
// ch := make(chan int, 2)
// ch <- 10
// ch <- 20
// ch <- 30 // Bufer to‘la va qabul qiluvchi yo‘q: deadlock.
Amaliy misol: cheklangan navbat
Quyidagi dastur ishlab chiqaruvchi va iste’molchini buferli channel orqali bog‘laydi.
| main.go | |
|---|---|
Natija:
Ishlab chiqaruvchi buferda joy boricha oldinga o‘tishi mumkin. Navbat to‘lganda iste’molchi qiymat olguncha bloklanadi. produce barcha ishlarni yuborgach channelni yopadi. range buferdagi qolgan qiymatlarni ham olib, keyin tugaydi.
Bufer ishlab chiqaruvchi va iste’molchi tezligini vaqtincha ajratadi. Iste’molchi doimiy ravishda sekin bo‘lsa, bufer oxir-oqibat to‘ladi va ishlab chiqaruvchi bloklanadi. Bu teskari bosim (backpressure) hosil qiladi va cheksiz xotira o‘sishining oldini oladi.
Sig‘imni qanday tanlash kerak?
Bufer hajmi tasodifiy katta son bo‘lmasligi kerak. U navbatda qancha tugallanmagan ishga ruxsat berilishini ifodalaydi.
Sig‘imni tanlashda quyidagilarni hisobga oling:
- iste’molchi bir paytda qancha ishni qayta ishlay oladi;
- vaqtinchalik yuklama sakrashi qancha bo‘lishi mumkin;
- har bir qiymat qancha xotira egallaydi;
- ortiqcha navbat natijaning eskirishiga olib keladimi;
- tizim sekinlashganda yuboruvchi bloklanishi kerakmi.
Juda kichik bufer keragidan ortiq bloklanish keltirishi mumkin. Juda katta bufer yuklama muammosini kechiktiradi, xotira sarfini oshiradi va eski ishlarni navbatda ushlab turadi. Avval kerakli semantikani belgilang, so‘ng o‘lchov va yuklama testi bilan sig‘imni tekshiring.
len va cap
cap(ch) channel buferining sig‘imini qaytaradi. Bufersiz channel uchun u 0. len(ch) esa hozir buferda turgan qiymatlar sonini beradi.
| main.go | |
|---|---|
Natija:
Concurrent dasturda len(ch) faqat o‘sha ondagi holatni ko‘rsatadi. Tekshiruvdan keyin boshqa goroutine qiymat yuborishi yoki olishi mumkin. Shu sabab if len(ch) < cap(ch) tekshiruvi keyingi send bloklanmasligini kafolatlamaydi.
Odatda dastur mantig‘ini len(ch)ga bog‘lamang. Bloklamaydigan send yoki receive kerak bo‘lsa, select va default ishlatiladi. Bu keyingi darsda ko‘rib chiqiladi.
Yo‘nalishli channel turlari
Oddiy chan T ikki tomonlama channel: unga qiymat yuborish ham, undan qabul qilish ham mumkin. Funksiya parametrida ruxsatni toraytirish mumkin:
chan<- int— faqat yuborish uchun channel;<-chan int— faqat qabul qilish uchun channel;chan int— ikki tomonlama channel.
Ikki tomonlama channel funksiyaga uzatilganda mos yo‘nalishli turga avtomatik torayadi:
Faqat qabul qiluvchi channelga yuborish yoki faqat yuboruvchi channeldan qabul qilish kompilyatsiya xatosi beradi. Bu API niyatini ko‘rsatadi va channelni noto‘g‘ri ishlatishning oldini oladi.
Faqat qabul qilish uchun berilgan channelni yopib bo‘lmaydi. Channelni yopish yuboruvchi tomonning vazifasi bo‘lgani uchun close yuborish imkoniga ega channel talab qiladi.
Qaysi channel turini tanlash kerak?
| Holat | Mos tanlov |
|---|---|
| Yuboruvchi va qabul qiluvchi aynan almashuv paytida uchrashishi kerak | Bufersiz channel |
| Qisqa yuklama sakrashini cheklangan navbatda ushlash kerak | Buferli channel |
| Funksiya faqat qiymat ishlab chiqaradi | chan<- T parametr |
| Funksiya faqat qiymat iste’mol qiladi | <-chan T parametr |
Bufersiz channel “sekin”, buferli channel esa “tez” degan umumiy qoida yo‘q. To‘g‘ri tanlov talab qilinadigan sinxronizatsiya va navbat semantikasiga bog‘liq. Performance farqini aniq ish yukida benchmark bilan o‘lchash kerak.
Keng tarqalgan xatolar
Bufer deadlockni butunlay yo‘q qiladi deb o‘ylash
Bufer faqat sig‘imigacha sendni qabul qiluvchidan ajratadi. U to‘lgach send yana bloklanadi. Qabul qiluvchi hayot sikli va channel yopilishi baribir aniq bo‘lishi kerak.
Buferni cheksiz kattalashtirish
Katta bufer sekin iste’molchini tuzatmaydi. U muammoni kechiktiradi va xotira hamda latency sarfini oshiradi. Tizimga mos limit va backpressure belgilang.
FIFO global bajarilish tartibini beradi deb o‘ylash
FIFO bitta channelga muvaffaqiyatli yuborilgan qiymatlar tartibiga tegishli. Turli goroutinelarning scheduler tartibi kafolatlanmaydi.
len(ch) bilan sinxronizatsiya qilish
len natijasi darhol eskirishi mumkin. Qarorni channel amali yoki select orqali atomar tarzda ifodalang.
Funksiya parametrida ortiqcha huquq berish
Faqat yuboradigan funksiyaga chan T berish uning tasodifan qabul qilishiga ham imkon beradi. API da eng tor chan<- T yoki <-chan T turini ishlating.
Interviewda nimalarga e’tibor beriladi?
- bufersiz send mos receive tayyor bo‘lguncha bloklanadi;
- buferli send faqat buferda joy boricha qabul qiluvchisiz davom etadi;
- channel FIFO, ammo turli goroutinelarning send tartibi kafolatlanmaydi;
- bufer cheklangan navbat va backpressure vositasi;
len(ch)sinxronizatsiya kafolati bermaydi;chan<- Tfaqat yuborish,<-chan Tfaqat qabul qilish uchun;- channel sig‘imi performance taxmini bilan emas, kerakli semantika va o‘lchov bilan tanlanadi.
Xulosa
Bufersiz channel yuboruvchi va qabul qiluvchini qiymat almashish vaqtida sinxronlashtiradi. Buferli channel ular orasiga cheklangan FIFO navbat qo‘yadi. Yo‘nalishli channel esa funksiya huquqlarini tur darajasida cheklaydi.
Bufer hajmini aniq limit sifatida tanlang. U sekin iste’molchini yoki noto‘g‘ri hayot siklini yashirish vositasi emas. Keyingi darsda bir nechta channel amali orasidan tayyorini tanlash uchun select operatorini o‘rganamiz.