Go Dilinde Concurrency: Fan In ve Fan Out

MuharremCandan
5 min readMar 15, 2024

--

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:

  1. Birden fazla veri gönderen kanal (chans...) tanımlanır.
  2. Bir çıkış kanalı (out) oluşturulur.
  3. Ayrı bir goroutine içerisinde select construct'u kullanılır.
  4. select içerisinde her veri gönderen kanaldan (ch) gelen değer (v) için bir case blok oluşturulur.
  5. Eğer değer okunursa (case out <- v), okunan değer çıkış kanalına (out) gönderilir.
  6. 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:

  1. Veri gönderen bir kanal (ch) tanımlanır.
  2. İşçi sayısı (workers) belirlenir.
  3. İşçi sayısı kadar yeni kanal (chans) oluşturulur.
  4. Her işçi için ayrı bir goroutine başlatılır.
  5. 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 👋

--

--

MuharremCandan

Hayalleri hayatının kaptanlığını yapan, limandan daha taptaze yazılım serüvenine yelken açan bir gemide Miço.