Sobes.tech
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 GOMAXPROCS ile 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:

  1. Yeni bir goroutine oluşturulur ve kullanılabilir bir P'nin yerel kuyruğuna konur.
  2. P ile ilişkili M, yerel kuyruktan bir goroutine alır ve çalıştırır.
  3. Eğer yerel kuyruk boşsa, M diğer P'lerden goroutine'leri çalmaya çalışır.
  4. 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.
  5. 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.
  6. 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.