Skip to content

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:

Men goroutineman...
Men goroutine emasman...

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:

Men goroutine emasman...

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:

Men goroutineman... 3
Men goroutineman... 1
Men goroutineman... 2
Men goroutine emasman...

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:
MenGoroutineman(1)
MenGoroutineman(2)
MenGoroutineman(3)
  • Har bir go chaqiruvi mustaqil ravishda orqa fonda(background)da ishga tushadi.
  • main() esa time.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:

Salom! 4
Salom! 3
Salom! 0
Salom! 1
Salom! 2
Main tugadi...