Goroutine, Go runtime'ı tarafından yönetilen hafif bir yürütme ipliğidir. Yeni bir goroutine başlatmak için, bir işlev çağrısının önüne go anahtar kelimesi eklenir. Arka planda, bir yığın ve görev durumunu tanımlayan bir yapı oluşturulup, goroutine planlayıcısının kuyruğuna eklenir.
İşletim sisteminin thread'lerinden farklı olarak, goroutine'nin başlangıç yığını çok daha küçüktür (genellikle 2 KB) ve gerektiğinde otomatik olarak genişler. Go planlayıcısı, bunları işletim sisteminin mevcut thread'lerine dağıtır (M:N modeli).
Thread'lerden (threads) ana farklılıklar:
package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d job %d başladı\n", id, j) time.Sleep(time.Second) fmt.Printf("worker %d job %d bitti\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 5) results := make(chan int, 5) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 5; j++ { jobs <- j } close(jobs) for a := 1; a <= 5; a++ { <-results } }
Go'nun goroutine'leri birden fazla çekirdekte paralel çalışabilir mi?
Sıkça yanlış cevap alınır: "Hayır, çünkü Go yeşil thread'ler kullanıyor." Aslında, ortam değişkeni veya runtime.GOMAXPROCS(n) çağrısıyla, Go goroutine'lerin yürütülmesini mevcut tüm işlemci çekirdeklerinde paralelleştirebilir.
import "runtime" func main() { runtime.GOMAXPROCS(4) // 4 çekirdek kullanmaya izin verir ... }
Hikaye
Go ile bir arka uç hizmet projesinde goroutine'ler aracılığıyla bir işçi havuzu oluşturuldu, ancak programcılar eşzamanlı çalışan goroutine'lerin sayısını sınırlamayı unuttular. Sonuç olarak, yük arttıkça uygulama binlerce goroutine başlatıyor, bu da bellek tükenmesine ve hizmetin çökmesine yol açıyordu. Sorun, aktif goroutine'ler için bir sınır getirilerek çözüldü (örneğin, bir semaphore veya işçi havuzu aracılığıyla).
Hikaye
Bir çalışan, goroutine'ler arasındaki verileri mutex veya kanallar olmadan sıradan global değişkenleri kullanarak yanlış bir şekilde senkronize etti. Bu, veri yarışına (race condition) yol açtı ve zaman zaman ödeme işlemlerinde hatalar meydana geldi. Sorun, yalnızca üretimde çalıştırıldığında tespit edildi.
Hikaye
Bir analiz hizmetinde, select deyiminde nil kanallarının iletimini atlamak gibi bir detay gözden kaçtı: kanal kapatıldığında select hala verileri beklemeye devam ediyor ve bazı goroutine'ler "takılıyordu". Sorun, kapalı kanala nil ataması yaparak ve select'in doğru işlenmesini sağlayarak düzeltildi.