Middle+
Go-da goroutine-lərin planlaşdırılması haqqında danışın.
sobes.tech Süni İntellekt
AI-dan cavab
Go'daki goroutine planlayıcısı, M:N modelini kullanır; burada M goroutine, N işletim sistemi ipine karşılık gelir. Goroutine'leri ipler arasında verimli bir şekilde değiştirir ve kesintisiz çoklu görev (multitasking) sağlar, durma noktaları (preemption points) ile.
GPM modelinin temel bileşenleri:
- G (Goroutine): Çalıştırılan kod birimi, hafif ip, Go planlayıcısı tarafından yönetilir.
- P (İşlemci): Mantıksal işlemci, yerel goroutine kuyruğu ve mutexler ile planlayıcı gibi kaynakları temsil eder. P sayısı varsayılan olarak işlemcinin mantıksal çekirdek sayısı ile belirlenir, ancak
GOMAXPROCSile değiştirilebilir. - M (Makine/İp): İşletim sistemi ipi, kodu yürütür. M, P ile ilişkilidir ve P'nin yerel kuyruğundan veya küresel kuyruktan goroutine'leri çalıştırır.
Çalışma prensibi:
- Yeni bir goroutine oluşturulur ve kullanılabilir bir P'nin yerel kuyruğuna konur.
- P ile ilişkili M, yerel kuyruktan bir goroutine alır ve çalıştırır.
- Eğer yerel kuyruk boşsa, M diğer P'lerden goroutine'leri çalmaya çalışır.
- Eğer P engellenirse (örneğin, sistem çağrısı sırasında), ona bağlı M ayrılır ve başka bir M bu P ile ilişkilendirilir. Engellenmiş M, sistem çağrısı tamamlandığında tekrar kullanılabilir.
- Eğer bir goroutine kanal veya mutex üzerinde engellenirse, bu goroutine bekleme kuyruğuna alınır ve planlayıcı, aynı M üzerinde başka bir goroutine seçer.
- Planlayıcı, uzun süre çalışan goroutine'leri zorlaştıma (preemption) noktalarıyla durdurmak için periyodik kontroller yapar, böylece diğer goroutine'ler CPU ve zamanı alabilir.
Preemption noktaları belirli yerlerde ortaya çıkar:
- Fonksiyon/method çağrıları.
- Fonksiyon çağrısı içeren döngüler.
- Bellek tahsisi.
- Senkronizasyon primitive'leri (kanallar, mutexler).
Planlama örneği:
package main
import (
"fmt"
"runtime"
"time"
)
func worker(id int) {
fmt.Printf("Goroutine %d çalışmaya başladı\n", id)
time.Sleep(time.Second) // Çalışmayı taklit et
fmt.Printf("Goroutine %d çalışmayı bitirdi\n", id)
}
func main() {
runtime.GOMAXPROCS(2) // Mantıksal işlemci sayısını ayarla
for i := 1; i <= 5; i++ {
go worker(i) // Goroutine'ler oluştur
}
time.Sleep(3 * time.Second) // Goroutine'lerin tamamlanmasını bekle
fmt.Println("Ana goroutine sona erdi")
}
Bu örnekte, 5 goroutine, 2 mantıksal işlemciye planlanır. Go planlayıcısı, bu goroutine'leri kullanılabilir işletim sistemi ipleri arasında dağıtacak, yerel ve muhtemelen küresel kuyruklar ile "iş çalma" (work stealing) mekanizmasını kullanacaktır.
Bu yaklaşımın avantajları:
- Ölçeklenebilirlik: Çok çekirdekli sistemlerin verimli kullanımı.
- Düşük maliyet: İşletim sistemi iplerine göre hafif goroutine'ler.
- Kullanım kolaylığı: Dile entegre planlama, paralel programlamayı daha erişilebilir kılar.