Sobes.tech
Middle+

Go'da goroutine planlaması hakkında bilgi verin.

sobes.tech yapay zeka

AI'dan gelen yanıt

Go'daki goroutine planlayıcısı, M:N modelini kullanır; burada M goroutine, N işletim sistemi iş parçacığına haritalanır. Goroutine'leri iş parçacıkları 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 iş parçacığı, Go planlayıcısı tarafından yönetilir.
  • P (İşlemci): Mantıksal işlemci, yerel kuyruk bağlamını ve mutexler ile planlayıcı gibi kaynakları temsil eder. Varsayılan P sayısı, işlemcinin mantıksal çekirdek sayısı ile belirlenir, ancak GOMAXPROCS ile değiştirilebilir.
  • M (Makine/İş Parçacığı): İşletim sistemi iş parçacığı, 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 çalmaya çalışır.
  4. P engellendiğinde (ö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. Bir goroutine kanal veya mutex üzerinde engellendiğinde, 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 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 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 iş parçacıkları 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 iş parçacıklarına göre hafif goroutine'ler.
  • Kullanım kolaylığı: Dile entegre planlama, paralel programlamayı daha erişilebilir kılar.