Go’da channel bilan ishlash
Channel — goroutinelar orasida ma’lum turdagi qiymatni uzatish va ularning ishini sinxronlashtirish vositasi. Bir goroutine qiymat yuboradi, boshqa goroutine uni qabul qiladi.
Masalan, bir nechta goroutine tashqi APIlardan ma’lumot olsa, natijalarni channel orqali yig‘uvchi goroutinega uzatishi mumkin. Bu umumiy map yoki o‘zgaruvchiga bir paytda yozish ehtiyojini kamaytiradi.
Channel umumiy xotiradagi obyektga xavfsiz pointer yuborishi mumkin, lekin pointer ko‘rsatgan obyektni keyinchalik bir nechta goroutine o‘zgartirsa, data race baribir yuz berishi mumkin. Channel qiymat uzatishni sinxronlashtiradi; u yuborilgan obyektni avtomatik himoya qilmaydi.
Channel yaratish
Channel turi chan kalit so‘zi va element turidan tuziladi:
Bu yerda chning turi chan int, ammo uning qiymati nil. Ishlaydigan channel odatda make bilan yaratiladi:
Bu bufersiz int channel. Unga faqat int qiymat yuborish va undan faqat int qiymat olish mumkin. Channel tur xavfsiz: string qiymatni chan intga yuborish kompilyatsiya xatosi beradi.
Kanal yaratish uchun alohida paket import qilinmaydi. make Go’ning ichki funksiyasi.
Yuborish va qabul qilish
<- operatori qiymat oqimining yo‘nalishini ko‘rsatadi:
ch <- value // value qiymatini ch channeliga yuborish
value := <-ch // ch channelidan qiymat qabul qilish
Yuborish va qabul qilish bloklovchi amallardir. Bufersiz channelda yuboruvchi va qabul qiluvchi bir-biriga tayyor bo‘lguncha kutadi. Bu qiymat uzatish bilan birga sinxronizatsiya ham hosil qiladi.
Birinchi misol
| main.go | |
|---|---|
Natija:
main bufersiz channel yaratib, square funksiyasini yangi goroutineda ishga tushiradi. chan<- int parametr funksiyaga faqat yuborishga ruxsat beradi. value := <-results natija kelguncha main goroutinesini kutdiradi.
square qiymat yuborganda send va receive bir nuqtada uchrashadi. Shundan keyin ikkala goroutine davom etishi mumkin. Alohida WaitGroup kerak emas, chunki natijani qabul qilishning o‘zi shu ish tugashini sinxronlashtiradi.
Bir nechta natijani yig‘ish
Quyidagi dastur uchta mustaqil hisobni goroutinelarda bajaradi. Natijalar tugash tartibida channelga keladi.
Natija:
Har bir ishchi result qiymatini bitta channelga yuboradi. Alohida goroutine barcha ishchilar tugashini WaitGroup bilan kutadi va keyin channelni yopadi. for item := range results channel yopilib, yuborilgan qiymatlar tugaguncha qabul qiladi.
Natijalar channelga turli tartibda kelishi mumkin. Dastur ularni mapga yig‘ib, keyin numbers tartibida chiqargani uchun ko‘rsatilgan natija barqaror.
wg.Wait()ni mainning o‘zida rangedan oldin chaqirish deadlock keltirishi mumkin edi: ishchilar bufersiz channelga yuborishda qabul qiluvchini kutadi, main esa ishchilarni kutib qoladi. Shu sabab kutish va close alohida goroutineda bajarilgan.
Channelni yopish
close(ch) endi channelga boshqa qiymat yuborilmasligini bildiradi. U channelni xotiradan o‘chirmaydi va qabul qiluvchilarni to‘xtatish buyrug‘i emas.
Channelni odatda qiymat ishlab chiqaruvchi tomon yopadi. Bir nechta yuboruvchi bo‘lsa, ularning barchasi tugaganini biladigan muvofiqlashtiruvchi goroutine yopadi. Qabul qiluvchi odatda channelni yopmasligi kerak, chunki u boshqa yuboruvchilar tugaganini bilmasligi mumkin.
Yopiq channelda quyidagi qoidalar ishlaydi:
- buferda qolgan qiymatlarni qabul qilish mumkin;
- qiymatlar tugagach, receive darhol element turining zero value qiymatini qaytaradi;
- yopiq channelga yuborish panic keltiradi;
- channelni ikkinchi marta yopish panic keltiradi;
nilchannelni yopish ham panic keltiradi.
Channel yopilganini zero value qiymatidan ajratish uchun ikki qiymatli receive ishlatiladi:
ok == false bo‘lsa, channel yopilgan va buferi bo‘sh. value esa element turining zero value qiymati bo‘ladi. Agar 0 channel orqali haqiqiy ma’lumot sifatida yuborilishi mumkin bo‘lsa, faqat value == 0 tekshiruvi yopilishni aniqlay olmaydi.
range bilan qabul qilish
Channel yopilguncha qiymatlarni olish uchun range qulay:
Channel yopilmasa, sikl keyingi qiymatni kutishda davom etadi. range ishlatish uchun channelni har doim yopish shart degan qoida yo‘q; faqat qabul qiluvchi oqim tugaganini yopilish orqali bilishi kerak bo‘lsa, yopish zarur.
nil channel
Channelning zero value qiymati nil:
nil channelga yuborish ham, undan qabul qilish ham doimiy bloklanadi. Bu ishga tayyor, lekin bo‘sh channel bilan bir xil emas. make(chan int) bo‘sh bo‘lsa ham haqiqiy channel va qarshi tomon paydo bo‘lganda davom etadi.
nil channel ba’zan select ichidagi holatni vaqtincha o‘chirish uchun ataylab ishlatiladi. Bu usul select darsida batafsil ko‘rib chiqiladi.
Channel va xotira sinxronizatsiyasi
Qiymatni channelga yuborish tegishli qabul qilish bilan sinxronizatsiya hosil qiladi. Yuboruvchi senddan oldin bajargan yozuvlar receive tugagach qabul qiluvchi goroutinega ko‘rinadi.
Bu qoida bir obyektni keyinchalik ikkala goroutine bir paytda o‘zgartirishga ruxsat bermaydi. Masalan, channel orqali slice yuborilgach yuboruvchi ham, qabul qiluvchi ham uning bir xil backing arrayiga yozsa, data race bo‘lishi mumkin. Amaliy qoida: qiymat yuborilgach uning egaligini qabul qiluvchiga topshiring yoki umumiy mutationni alohida himoya qiling.
Deadlockka olib keladigan holat
Quyidagi dasturda bufersiz channelga yuborish bor, ammo qabul qiluvchi yo‘q:
Runtime odatda quyidagiga o‘xshash xabar bilan dasturni to‘xtatadi:
main send amalida bloklanadi. Boshqa davom eta oladigan goroutine yo‘qligi uchun runtime deadlockni aniqlaydi. Real serverda ayrim goroutinelar tarmoq yoki taymerni kutayotgan bo‘lsa, mantiqiy deadlock har doim ham shu runtime xabari bilan darhol aniqlanmasligi mumkin.
Keng tarqalgan xatolar
Channelni noto‘g‘ri tomon yopishi
Qabul qiluvchi channelni yopsa, hali ishlayotgan yuboruvchi panic qilishi mumkin. Yopish mas’uliyatini yuboruvchi yoki barcha yuboruvchilar tugaganini biladigan koordinatorda saqlang.
Har bir channelni majburan yopish
Garbage collector foydalanilmaydigan channelni yig‘a oladi. Channelni xotirani bo‘shatish uchun emas, qabul qiluvchiga boshqa qiymat kelmasligini bildirish kerak bo‘lganda yoping.
Yopiq channeldan cheksiz receive qilish
Oddiy receive yopiq channeldan zero value qaytarishda davom etadi. Oqim tugashini bilish uchun value, ok := <-ch yoki for range ishlating.
Channel data raceni butunlay yo‘q qiladi deb o‘ylash
Channel orqali pointer, slice yoki map yuborish ichki ma’lumotni nusxalamaydi. Egalikni aniq belgilang yoki concurrent mutationni himoya qiling.
Interviewda nimalarga e’tibor beriladi?
- send va receive bloklovchi amallar;
- channel qiymat uzatish bilan birga sinxronizatsiya hosil qiladi;
- yopiq channelga send panic qiladi, receive esa qolgan qiymatlar va keyin zero value qaytaradi;
value, ok := <-chyopilish holatini ajratadi;- channelni odatda yuboruvchi yopadi;
nilchannelga send va receive doimiy bloklanadi;- channel yuborilgan reference obyektni keyingi concurrent mutationdan avtomatik himoya qilmaydi.
Xulosa
Channel goroutinelar orasida tur xavfsiz qiymat uzatadi va ularning bajarilishini sinxronlashtiradi. ch <- value yuboradi, <-ch qabul qiladi, close(ch) esa boshqa qiymat kelmasligini bildiradi.
Channel egasi, uni kim yopishi va qabul qiluvchi qachon to‘xtashi oldindan aniq bo‘lishi kerak. Keyingi darsda bufersiz, buferli va yo‘nalishli channel turlarining farqini ko‘rib chiqamiz.