Goroutine
Goroutinelar Goning konkurrentlik (bir vaqtning o'zida ko'p ishlarni bajarish) uchun mo'ljallangan asosiy xususiyatlaridan biri. Goroutinelar ancha yengil bo'lib, Go runtime tomonidan boshqariladi. Shu sababli yuzlab yoki minglab goroutinelar isha tushirilsa ham katta resurs talab qilmasdan ishlaydi.
Go runtime nimalar qiloladi?
- Goroutinelarni yaratish, ularga resurslarni (xotira, CPU va boshqalar) taqsimlash.
- Goroutinelarni navbat bilan ishlatish (scheduler).
- Stack o'sishini boshqarish.
- Garbage collection (keraksiz xotirani tozalash).
kabi vazifalarni o'z zimmasiga oladi.
Info
Stack — bu har bir goroutine uchun ajratilgans xotira bo'lagi. U yerda goroutine ichida ishlatiladigan o'zgaruvchilar va funksiyalar haqida ma'lumot saqlanadi. Stack boshida juda kichik bo'ladi (taxminan 2 KB), lekin kerak bo'lsa o'zi avtomatik kengayadi.
Info
Garbage collection (GC) — bu dasturda kerak bo'lmay qolgan ma'lumotlarni xotiradan avtomatik olib tashlaydigan tizimi. Dastur ishlaganda yangi o'zgaruvchilar va obyektlar yaratiladi. Agar ular kerak bo'lmay qolsa, GC ularni o'zi topadi va xotirani bo'shatadi.
Goroutinening ba'zi xususiyatlari:
- Boshlang'ich stack hajmi juda kichik bo'ladi (odatda taxminan 2 KB atrofida).
- Go runtime stack hajmini zaruratga qarab avtomatik oshirib boradi, an’anaviy threadlar kabi oldindan katta stack ajratish shart emas.
- Goroutineni operatsion tizim emas, balki Go runtime o'zi rejalashtiradi, bu esa kontekst almashinuvga (context switching) ajratiladigan resurs sarfini sezilarli darajada kamaytiradi.
OS (Operatsion tizim) threadlar bilan ishlashda, operatsiyalarni ko'p resurs evaziga bajarishi kerak bo'ladi, lekin Go runtime bu ishlarni oson va samarali tarzda kam resurs bilan amalga oshiradi.
Context switching nima?
Context switching — bu kompyuter yoki operatsion tizim bir ishni (masalan, bir dastur yoki bir vazifa) to'xtatib, boshqa bir ishga o'tishi jarayoni.
Ya'ni:
- Kompyuter hozir A vazifani (masalan, hisob-kitob) bajarayotgan bo'ladi.
- Lekin bir vaqtda B vazifa (masalan, chatdan xabar kelishi) ham ishlashi kerak.
- Shunda operatsion tizim A vazifani pauza qiladi, uning holatini (registerlar, stack, memory va hokazolarni) saqlaydi,
- Keyin B vazifaning ishga tushiradi, uni davom ettiradi.
Hayotimizdan misol:
Telefon orqali gaplashyapsiz (vazifa A), shu payt SMS xabar keladi (vazifa B). Bir soniyaga gaplashishni to'xtatib, SMSni o'qib, keyin yana gaplashishni davom ettirasiz. Bu — context switching bo'ladi.
Info
Context switching — kompyuter qaysi ishni qayerda to'xtatganini eslab qolishi va keyin boshqa ishga o'tish jarayoni.
Go bilan qanday ishlaydi?
Go da goroutinelar o'zaro almashganda, Go runtime eski goroutinening kontekstini saqlab, yangi goroutinening kontekstini yuklaydi.
Goroutine amaliyot
Funksiyalarni goroutine bilan ishga tushirish uchun go kalit so'zidan foydalaniladi.
package main
import (
"fmt"
"time"
)
func main() {
go MenGoroutineman()
time.Sleep(time.Second)
fmt.Println("Men goroutine emasman...")
}
func MenGoroutineman() {
fmt.Println("Men goroutineman...")
}
Natija:
Bu yerda MenGoroutineman
funksiyasi yangi goroutine sifatida ishga tushirilmoqda. time.Sleep(time.Second)
esa main
funksiyasini 1 soniya davomida ushlab turadi. Agar Sleep
bo'lmasa, main
funksiyasi juda tez tugab qoladi va goroutine
ishga ulgurmay, dastur o'z ishini yakunlaydi. Go dasturlash tilida ish main()
funksiyasidan boshlanadi, main()
funksiyasi ishini yakunlasa butun dastur ishini yakunlaydi. Chunkis Goda asosiy goroutine bu main()
hisoblanadi,
Goroutine esa asosiy goroutine ustida ishlaydi.
Info
main()
funksiyasi tugashi bilan barcha hatto ishlayotgan goroutinelar ham avtomatik ravishda to'xtatiladi.
Goroutine
va main
funksiyasi o'zlari mustaqil bajarilsa ham, dastur hayoti faqat main
funksiyasining tugashiga
bog'liqdir. main tugasa, barcha narsa to'xtaydi.
Endi yuqoridagi misolimizdan vaqtincha ushlab turish qismini time.Sleep(time.Second)
olib tashlaymiz:
package main
import (
"fmt"
)
func main() {
go MenGoroutineman()
fmt.Println("Men goroutine emasman...")
}
func MenGoroutineman() {
fmt.Println("Men goroutineman...")
}
Natija:
Nima uchun bu natijani olganimiz tushunarli deb o'ylayman. Ha tushunganingizdeb bu yera main
o'z ishini yakunladi
goroutine esa ishga tushishga ulgurmadi.
Keling endi bir nechta goroutine larni bir vaqtda ishga tushirib ko'ramiz, tushunishimiz yaxshi bo'lishi uchun funksiyaga
ozroq o'zgartirish kiritamiz. Goroutine tartibini belgilashimiz uchun funksiyaga n
parametr qo'shamiz.
package main
import (
"fmt"
"time"
)
func main() {
go MenGoroutineman(1)
go MenGoroutineman(2)
go MenGoroutineman(3)
time.Sleep(time.Second)
fmt.Println("Men goroutine emasman...")
}
func MenGoroutineman(n int) {
fmt.Println("Men goroutineman...", n)
}
Natija:
Sizda natija boshqacha bo'lgan bo'lishi mumkin agar bunday bo'lmasa dasturni qayta ishga tushiring. Sababini quyida ko'ramiz:
main()
funksiyasi 3 ta go operatori bilan goroutinelarni ishga tushiradi:
- Har bir go chaqiruvi mustaqil ravishda orqa fonda(background)da ishga tushadi.
main()
esatime.Sleep(time.Second)
bilan 1 soniya kutadi ya'ni goroutinelarga ishlash uchun vaqt beriladi.- Goroutinelarning ishga tushish tartibi kafolatlanmagan bo'ladi. Ular qaysi biri tezroq rejalashtirilsa (scheduled), o'sha birinchi ishlaydi. Bu tizimning ishiga (scheduler) bog'liq. Shu sababli sizda natija boshqacha bo'lishi mumkin.
for bilan misol
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go salom(i)
}
time.Sleep(2 * time.Second)
fmt.Println("Main tugadi...")
}
func salom(i int) {
fmt.Println("Salom!", i)
}
Natija: