Tarkibga o'tish

7. Goroutine va kanallar

Goroutine — yengil oqim, kanal (chan) — goroutine'lar orasida ma'lumot uzatish vositasi. Barcha masalalarni go run -race bilan ishga tushirib, poyga holati (data race) yo'qligini tekshiring.

Goroutine asoslari

1. Uchta goroutine ishga tushiring, har biri o'z raqamini 5 marta chiqarsin. main goroutine tugashini kutmasa nima bo'lishini ko'rsating, so'ng sync.WaitGroup bilan kutishni qo'shing.
Misol: main kutmasa — odatda hech narsa chiqmaydi yoki juda kam qator chiqadi; WaitGroup bilan jami 15 qator (1, 2, 3 raqamlari, tartibi har safar farq qilishi mumkin)

2. Sikl ichida goroutine ishga tushirishda sikl o'zgaruvchisidan foydalanishni ko'rsating (Go 1.22+ dagi har iteratsiya uchun yangi o'zgaruvchi xatti-harakati bilan).
Misol: for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() } → Go 1.22+ da 0, 1, 2 (tartibi farq qilishi mumkin); Go 1.21 va undan eskida odatda 3 3 3

3. 1 dan N gacha sonlar yig'indisini K ta goroutine'ga bo'lib hisoblang: har biri o'z oralig'ini hisoblasin, natijalar kanal orqali yig'ilsin.
Misol: N = 100, K = 4 → har biri 25 tadan oraliq hisoblaydi, jami 5050

4. runtime.NumGoroutine() yordamida ishga tushirilgan va tugamay qolgan goroutine'lar sonini kuzating; qasddan «oqib ketgan» (hech qachon tugamaydigan) goroutine misolini yarating va uni tuzating.
Misol: boshida runtime.NumGoroutine() → 1; hech qachon o'qilmaydigan kanalga yozuvchi 3 ta goroutine ishga tushirilsa → 4; tuzatilgach (kanal yopiladi yoki context bilan to'xtatiladi) → 1

Kanallar

5. Buferlanmagan kanal orqali bitta goroutine'dan ikkinchisiga 10 ta son yuboring. Yuboruvchi va qabul qiluvchi qanday sinxronlashishini izohlang.
Misol: 1 … 10 yuboriladi → qabul qiluvchi 1 2 3 4 5 6 7 8 9 10 ni shu tartibda chiqaradi

6. Buferlangan kanal (make(chan int, 3)) ga 3 ta qiymat bloklanmasdan yozilishini, 4-si esa bloklanishini ko'rsating. len va cap ni chiqaring.
Misol: make(chan int, 3): len = 0, cap = 3; 3 ta yozuvdan keyin len = 3, cap = 3; 4-yozuv o'qiydigan goroutine bo'lmasa bloklanadi (fatal error: all goroutines are asleep - deadlock!)

7. Ishlab chiqaruvchi kanalni close bilan yopsin; iste'molchi for v := range ch bilan barcha qiymatlarni o'qisin. Yopilgan kanaldan o'qish va unga yozish natijalarini ko'rsating.
Misol: 1 2 3 yuborilib close(ch): for v := range ch → 1 2 3; yopilgan kanaldan o'qish → 0 false; yopilgan kanalga yozish → panic: send on closed channel

8. Faqat yuborish (chan<- int) va faqat qabul qilish (<-chan int) yo'nalishli kanal parametrlaridan foydalanuvchi funksiyalar yozing.
Misol: producer(out chan<- int) 1 2 3 yuboradi, consumer(in <-chan int) → 1 2 3

9. v, ok := <-ch yordamida kanal yopilganligini aniqlang.
Misol: yopilgunicha v, ok := <-ch → 5 true; yopilgandan keyin → 0 false

10. «Ping-pong»: ikki goroutine bitta kanal orqali navbat bilan N marta xabar almashsin.
Misol: N = 3 → ping 1, pong 1, ping 2, pong 2, ping 3, pong 3

select

11. Ikki kanaldan qaysi biri birinchi qiymat yuborsa, o'shani o'qing (select). Kanallar turli kechikishlar bilan qiymat yuborsin.
Misol: ch1 100 ms dan, ch2 50 ms dan keyin yuborsa → birinchi bo'lib ch2 qiymati o'qiladi

12. select va time.After yordamida kutish muddatini (timeout) amalga oshiring: javob 1 soniyada kelmasa, «vaqt tugadi» deb chiqaring.
Misol: javob 2 s da kelsa → vaqt tugadi; 100 ms da kelsa → javob matni

13. default tarmog'i bilan bloklanmaydigan yuborish va qabul qilishni amalga oshiring.
Misol: band kanalga yuborishda select { case ch <- 1: default: } → kanal band; bo'sh kanaldan qabul qilishda → qiymat yo'q

14. time.Ticker yordamida har 200 ms da ish bajaruvchi va done kanali yopilganda to'xtovchi goroutine yozing.
Misol: 200 ms li Ticker, 1 s dan keyin done yopilsa → taxminan 5 ta «tik» va to'xtash

15. nil kanalning select dagi xususiyatidan foydalaning: ikki kanalni birlashtirib, ulardan biri yopilganda uni nil ga tenglab o'chirib qo'ying.
Misol: a = [1 2], b = [3] birlashtirilsa → 1 2 3 (tartib farq qilishi mumkin); ikkala kanal yopilgach sikl tugaydi

Konkurentlik naqshlari

16. Konveyer (pipeline): generate → square → print bosqichlaridan iborat zanjir tuzing, har bir bosqich alohida goroutine va kanal bilan.
Misol: generate(1, 2, 3, 4) → square → print → 1 4 9 16

17. Fan-out/fan-in: bitta kirish kanalidagi vazifalarni K ta ishchi goroutine'ga taqsimlang va natijalarni bitta chiqish kanaliga birlashtiring.
Misol: vazifalar 1 … 6 (kvadratga ko'tarish), K = 3 ishchi → natijalar 1 4 9 16 25 36 (tartibi farq qilishi mumkin)

18. Ishchilar hovuzi (worker pool): N ta URL yoki fayl nomini K ta ishchi bilan qayta ishlang; natijalar kirish tartibida chiqarilsin.
Misol: [a.txt b.txt c.txt], K = 2 ishchi → natijalar kirish tartibida: a.txt: …, b.txt: …, c.txt: …

19. Semafor: buferlangan kanal yordamida bir vaqtda ishlaydigan goroutine'lar sonini K ta bilan cheklang.
Misol: K = 2, 5 ta goroutine → bir vaqtda ko'pi bilan 2 tasi ishlaydi (hisoblagich bilan tekshiriladi)

20. Tezlik cheklovchi (rate limiter): so'rovlarni soniyasiga ko'pi bilan 5 ta qilib qayta ishlang (time.Tick asosida).
Misol: 10 ta so'rov, soniyasiga 5 ta → jami taxminan 2 s

21. Birinchi javobni olish: bir xil so'rovni uchta «server» ga parallel yuboring va birinchi kelgan javobni qaytaring; qolgan goroutine'lar oqib ketmasligini ta'minlang.
Misol: serverlar kechikishi 300, 100, 200 ms → server 2: javob (taxminan 100 ms da); qolganlari bekor qilinadi

22. Xatolar bilan parallel bajarish: har bir vazifa error qaytarsin; birinchi xato yuz berganda qolgan natijalarni kutmasdan xatoni qaytaring (errgroup ishlatmasdan).
Misol: 3 ta vazifa, 2-si xato qaytaradi → birinchi xato darhol qaytariladi, qolgan natijalar kutilmaydi

23. Parallel birlashtirib saralash (merge sort): katta slice'ni ikkiga bo'lib, har yarmini alohida goroutine'da saralang; bo'lak kichik bo'lsa, ketma-ket saralashga o'ting.
Misol: [5 2 8 1 9 3] → [1 2 3 5 8 9]

24. Katalogdagi barcha fayllarning qatorlar sonini parallel sanang va jami qatorlar sonini chiqaring.
Misol: katalogda 3 ta fayl: 10, 20, 5 qator → jami 35

25. Parallel tub sonlar g'alviri: har bir tub son uchun alohida filtr goroutine'i yaratiladigan klassik «concurrent prime sieve» ni yozing va dastlabki 100 ta tub sonni chiqaring.
Misol: dastlabki 10 ta tub: 2 3 5 7 11 13 17 19 23 29; 100-tub son 541

Amaliy masalalar

26. Parallel yuklovchi (fetcher): Fetcher interfeysi (Fetch(url string) (body string, err error)) va tasodifiy kechikish bilan javob beruvchi soxta amalga oshirish yozing. URL'lar ro'yxatini bir vaqtda ko'pi bilan 3 tadan «yuklab», har birining natijasi va xatosini chiqaring.
Misol: 6 ta URL, bir vaqtda ko'pi bilan 3 tadan → har bir URL uchun url: natija yoki url: xato; yuklanayotganlar soni hech qachon 3 dan oshmaydi

27. Hisob-kitob serveri modeli: mijozlar kanal orqali so'rov (Request{A, B int; Reply chan int}) yuboradi, server goroutine'i javobni Reply kanaliga qaytaradi.
Misol: Request{A: 2, B: 3} → Reply kanalidan 5

28. Chat xonasi modeli: join, leave va broadcast kanallari orqali ishlaydigan markaziy goroutine; har bir foydalanuvchi o'z kanaliga ega.
Misol: join(Ali), join(Vali), broadcast("Salom") → ikkalasi ham Salom oladi; leave(Ali) dan keyingi xabarni faqat Vali oladi

29. Bank hisobini bitta «egasi» goroutine orqali boshqaring: barcha Deposit va Balance so'rovlari kanal orqali yuboriladi (mutex'siz, «xotirani bo'lishish o'rniga muloqot qiling» tamoyili).
Misol: Deposit(100), Deposit(50), Balance() → 150

30. «Ovqatlanayotgan faylasuflar» masalasini kanallar yordamida o'lik blokirovkasiz (deadlock'siz) yeching.
Misol: 5 faylasuf, har biri 3 marta ovqatlanadi → barcha faylasuflar 3/3 ga yetadi, dastur deadlock'siz tugaydi