Go Dilinde Concurrency: Fan In ve Fan Out
Go dili, concurrency (eşzamanlılık) için güçlü araçlar sunar. Bu araçlardan ikisi de fan in ve fan out pattern’larıdır. Bu pattern’lar, birden fazla goroutine arasında veri akışını yönetmek için kullanılır.
Fan In
Fan In, Go dilinde birden fazla kaynaktan (genellikle kanallardan) gelen veriyi tek bir kanalda birleştirme işlemidir. Bu, birden fazla işlemin sonucunu toplamak veya farklı kaynaklardan gelen verileri işlemek için kullanılır.
Çalışma Prensibi:
- Birden fazla veri gönderen kanal (
chans...
) tanımlanır. - Bir çıkış kanalı (
out
) oluşturulur. - Ayrı bir goroutine içerisinde
select
construct'u kullanılır. select
içerisinde her veri gönderen kanaldan (ch
) gelen değer (v
) için bir case blok oluşturulur.- Eğer değer okunursa (
case out <- v
), okunan değer çıkış kanalına (out
) gönderilir. - Herhangi bir kanaldan veri okunamadığında ve
done
kanalından mesaj alınırsa (case <-done: return
), döngü sonlandırılır.
Avantajları:
- Birden fazla kaynaktan gelen veriyi tek bir noktada toplama imkanı sağlar.
- Paralel olarak çalışan goroutine’lerin çıktılarını birleştirmek için kullanılabilir.
- Kodun okunabilirliğini ve anlaşılırlığını artırır.
package main
import (
"fmt"
)
// fanIn fonksiyonu, bir "done" kanalı ve birden fazla veri kanalı alır ve
// tüm giriş kanallarındaki verileri birleştiren bir kanal döndürür.
func fanIn(done <-chan struct{}, chans ...<-chan int) <-chan int {
out := make(chan int) // Birleştirilmiş veriyi göndermek için bir kanal oluşturulur.
// Birleştirme işlemini işlemek için bir goroutine başlatılır.
go func() {
defer close(out) // Goroutine bittiğinde çıkış kanalını kapatır.
// Her bir giriş kanalı üzerinde döngüye girilir.
for _, ch := range chans {
for v := range ch { // Mevcut kanaldan veri okunur.
select {
case out <- v: // Veri çıkış kanalına gönderilir.
case <-done: // "done" sinyali alınırsa döngüden çıkılır.
return
}
}
}
}()
return out // Birleştirilmiş veri içeren kanalı döndürür.
}
func main() {
done := make(chan struct{}) // Tamamlanma sinyalini göndermek için kanal.
// İki veri kanalı oluşturulur.
ch1 := make(chan int)
ch2 := make(chan int)
// ch1'e veri göndermek için bir goroutine başlatılır.
go func() {
for i := 0; i < 10; i++ {
ch1 <- i
}
close(ch1) // Tüm veriler gönderildikten sonra ch1 kapatılır.
}()
// ch2'ye veri göndermek için bir goroutine başlatılır.
go func() {
for i := 10; i <= 90; i += 10 {
ch2 <- i
}
close(ch2) // Tüm veriler gönderildikten sonra ch2 kapatılır.
}()
// ch1 ve ch2'den gelen verileri birleştirmek için fanIn çağrılır.
out := fanIn(done, ch1, ch2)
// Birleştirilmiş veri yazdırılır.
for v := range out {
fmt.Println(v)
}
close(done) // Tamamlanma sinyali gönderilir
}
Not: Bu örnekte, done
kanalı deadlock senaryosunu önlemek için kullanılmaktadır. Herhangi bir kanaldan daha fazla veri gönderilmeyecekse ve done
kanalına mesaj gönderilmezse program sonsuz bir döngüye girebilir.
Fan In, Go dilinde verilerin farklı kaynaklardan birleştirilmesi gereken durumlarda sıklıkla kullanılan bir concurrency pattern’ıdır.
Fan Out
Fan out, Go dilinde tek bir kaynaktan gelen veriyi birden fazla goroutine (işçi) dağıtmak için kullanılan bir concurrency pattern’dır. Bu, bir işlemi paralel olarak yürütmek veya birden fazla işçiye aynı veriyi göndermek için faydalı bir yöntemdir.
Çalışma Prensibi:
- Veri gönderen bir kanal (
ch
) tanımlanır. - İşçi sayısı (
workers
) belirlenir. - İşçi sayısı kadar yeni kanal (
chans
) oluşturulur. - Her işçi için ayrı bir goroutine başlatılır.
- Her goroutine içerisinde:
- Kendi kanalına (
ch
) veri gönderilmeyi bekler (select
). - Veri alındığında, işlenir ve istenirse yazdırılır.
done
kanalından mesaj alındığında (select
), döngü sonlandırılır.
6. Veri gönderen goroutine, döngü içerisinde:
- Veri gönderen kanaldan (
ch
) veri okur. - Her işçi kanalına (
chans
) okuduğu veriyi göndermeye çalışır (select
). done
kanalından mesaj alınırsa (select
), döngü sonlandırılır.
Avantajları:
- İşleri paralelize ederek programın performansını artırır.
- Birden fazla işçiye aynı anda görev dağıtılmasını sağlar.
- Kodun okunabilirliğini ve anlaşılırlığını artırır.
package main
import (
"fmt"
)
// fanOut fonksiyonu, bir "done" kanalı, veri gönderen bir kanal ve işçi
// sayısını alır ve her işçiye veri gönderen birden fazla kanal döndürür.
func fanOut(done <-chan struct{}, ch <-chan int, workers int) []chan int {
chans := make([]chan int, workers) // İşçi sayısı kadar kanal oluşturulur.
// Her işçi için bir goroutine başlatılır.
for i := 0; i < workers; i++ {
chans[i] = make(chan int) // Her işçi için bir kanal oluşturulur.
go func(ch <-chan int, i int) {
for v := range ch { // Veri kanalından okunur.
select {
case <-done: // "done" sinyali alınırsa döngüden çıkılır.
return
default: // "done" sinyali alınmadıysa veri işlenir.
fmt.Println("İşçi", i, ":", v)
}
}
}(chans[i], i)
}
// Veri gönderen kanaldan veri okunarak her işçiye gönderilir.
go func() {
defer func() {
for _, ch := range chans {
close(ch) // Gönderici kanallar kapatılır.
}
}()
for v := range ch {
for _, ch := range chans {
select {
case ch <- v:
case <-done:
return
}
}
}
}()
return chans // İşçi kanallarından oluşan bir dilim döndürülür.
}
func main() {
done := make(chan struct{}) // Tamamlanma sinyali göndermek için kanal.
ch := make(chan int) // Veri gönderen kanal.
// Veri gönderen kanala 10 adet değer gönderilir.
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch) // Tüm veriler gönderildikten sonra kanal kapatılır.
}()
// 4 işçi ile fanOut işlemi gerçekleştirilir.
chans := fanOut(done, ch, 4)
// İşçi kanallarından gelen veriler yazdırılır.
for _, ch := range chans {
for v := range ch {
fmt.Println(v)
}
}
close(done) // Tamamlanma sinyali gönderilir.
}
Not:
done
kanalı, işçilerin tamamlanma sinyalini alması ve gereksiz yere çalışmaya devam etmelerini engellemek için kullanılır.- İşçi sayısı ihtiyaca göre artırılabilir veya azaltılabilir.
Fan out, Go dilinde concurrency ile çalışan programlarda yaygın olarak kullanılan bir pattern’dır.
Bu yazıda, Go dilinde fan in ve fan out pattern’larını inceledik. Bu pattern’ların ne olduğunu, ne işe yaradığını ve nasıl uygulandığını öğrendik. Bir sonraki yazıda buluşmak üzere, sağlıcakla kalın 👋