Go CSP Modeli

Git sarıl Sıralı Süreçlerin İletişimi (CSP)resmi bir model Tony Hoare tarafından 1978'de önerildi. Temel prensip: birden fazla goroutine sahip olmak yerine Aynı değişkeni okuyan ve ona yazan (mutex ile), goroutinler aracılığıyla iletişim kurar kanal — veri sahipliğini aktaran yazılı kanallar.

Go çalışma zamanı, bir işletim sistemi iş parçacığı havuzundaki goroutinlerin zamanlamasını yönetir (varsayılan olarak hatta tarafından kontrol edilen mevcut çekirdek sayısına göre GOMAXPROCS). Bir gorutin Bloklama işlemleri (G/Ç, kanal alımı) sırasında otomatik olarak askıya alınır ve diğerlerine izin verilir ilerlemek için goroutine.

Goroutine: Anatomi ve Yaşam Döngüsü

package main

import (
    "fmt"
    "time"
)

func worker(id int, done chan struct{}) {
    fmt.Printf("Worker %d started\n", id)
    time.Sleep(100 * time.Millisecond)     // simula lavoro
    fmt.Printf("Worker %d finished\n", id)
    done <- struct{}{}                       // segnala completamento
}

func main() {
    done := make(chan struct{}, 5)  // channel bufferizzato per 5

    // Lancia 5 goroutine concorrentemente
    for i := 0; i < 5; i++ {
        go worker(i, done)           // "go" avvia la goroutine
    }

    // Aspetta che tutte completino
    for i := 0; i < 5; i++ {
        <-done                       // riceve dal channel (blocca se vuoto)
    }

    fmt.Println("All workers done")
}

// Costo di una goroutine: ~2-8KB stack (cresce dinamicamente fino a 1GB)
// vs ~1MB per un OS thread
// Go può avere milioni di goroutine attive contemporaneamente

Kanal: Türler ve Desenler

Kanal Arabelleğe Alınmamış ve Arabelleğe Alınmış

// Channel unbuffered: sincronizzazione diretta
// Send blocca finché un receiver è pronto
ch := make(chan int)         // unbuffered
go func() { ch <- 42 }()  // blocca finché qualcuno riceve
v := <-ch                   // sblocca il sender

// Channel buffered: coda FIFO con capacità fissata
// Send blocca SOLO se il buffer è pieno
buffered := make(chan int, 10)   // buffer da 10 elementi
buffered <- 1                   // non blocca (buffer ha spazio)
buffered <- 2                   // non blocca
v := <-buffered                 // riceve 1 (FIFO)

// Directional channel types: sicurezza a compile time
func producer(ch chan<- int) { // solo write
    ch <- 42
}

func consumer(ch <-chan int) { // solo read
    v := <-ch
    fmt.Println(v)
}

Desen: Boru Hattı

// Pipeline: catena di goroutine connesse da channel
// Ogni fase legge dall'input channel e scrive sull'output channel

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)    // IMPORTANTE: chiudi quando finisci
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {  // range su channel riceve finché chiuso
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    // Compone la pipeline
    c := generate(2, 3, 4, 5)
    out := square(c)

    // Consuma l'output
    for v := range out {
        fmt.Println(v)  // 4, 9, 16, 25
    }
}

Seçin: Çoklu Kanallarda Çoğullama

select Go'da rekabet için en güçlü anahtar kelime: bekle aynı anda birden fazla kanalı çalıştırır ve kanal hazır durumunu yürütür. Birden fazla kanal varsa onlar hazır, rastgele seçiyor.

// Timeout pattern con select
import "time"

func fetchWithTimeout(url string, timeout time.Duration) (string, error) {
    resultCh := make(chan string, 1)
    errCh := make(chan error, 1)

    go func() {
        result, err := fetch(url)
        if err != nil {
            errCh <- err
            return
        }
        resultCh <- result
    }()

    select {
    case result := <-resultCh:
        return result, nil
    case err := <-errCh:
        return "", err
    case <-time.After(timeout):
        return "", fmt.Errorf("timeout after %v", timeout)
    }
}

// Fan-out/fan-in con select e done channel
func merge(channels ...<-chan int) <-chan int {
    merged := make(chan int)
    var wg sync.WaitGroup

    multiplex := func(ch <-chan int) {
        defer wg.Done()
        for v := range ch {
            merged <- v
        }
    }

    wg.Add(len(channels))
    for _, ch := range channels {
        go multiplex(ch)
    }

    go func() {
        wg.Wait()
        close(merged)
    }()

    return merged
}

context.Context: Yayılmış Silme

Paket context Go'nun silme işlemini yaymaya yönelik standart mekanizmasıdır bir goroutin zinciri aracılığıyla. G/Ç yapan veya uzun süreli çalışma yapan herhangi bir işlev bunu kabul etmelidir bir context.Context ilk parametre olarak:

import (
    "context"
    "fmt"
    "time"
)

// Funzione che rispetta la cancellazione
func longOperation(ctx context.Context, id int) error {
    for i := 0; i < 10; i++ {
        select {
        case <-ctx.Done():              // controlla se il context è cancellato
            return ctx.Err()           // errore: context.Canceled o DeadlineExceeded
        default:
            fmt.Printf("Step %d/%d\n", i+1, 10)
            time.Sleep(100 * time.Millisecond)
        }
    }
    return nil
}

func main() {
    // Context con timeout di 500ms
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()  // SEMPRE defer cancel() per liberare le risorse

    err := longOperation(ctx, 1)
    if err != nil {
        fmt.Printf("Cancelled: %v\n", err)  // context deadline exceeded
    }
}

// Context si propaga attraverso le chiamate:
// Handler HTTP -> Service -> Repository -> Database
// Se il client disconnette, il context si cancella e risale tutta la catena

WaitGroup: Senkronizasyon Modeli

import "sync"

func processAll(items []Item) {
    var wg sync.WaitGroup
    results := make([]Result, len(items))

    for i, item := range items {
        wg.Add(1)
        go func(i int, item Item) {   // passa i e item come parametri!
            defer wg.Done()
            results[i] = process(item) // scrivere indici diversi è safe
        }(i, item)
    }

    wg.Wait()           // blocca finché tutti Done() sono stati chiamati
    fmt.Println(results)
}

// ERRORE COMUNE: closure su variabile del loop (prima di Go 1.22)
// In Go 1.22+ il loop variable è scoped per iterazione -- no problema
for _, item := range items {
    go func() {
        process(item)    // Go 1.22+: safe. Go <1.22: BUG! usa go func(i Item) invece
    }()
}

Yarış Dedektörü: Yarış Verilerini Bulma

// Compila ed esegui con il race detector integrato
go run -race main.go
go test -race ./...

// Esempio di data race che il detector trova:
var counter int

func increment() {
    counter++  // DATA RACE! lettura + scrittura non atomica
}

// go run -race stamperà:
// WARNING: DATA RACE
// Write at 0x... by goroutine 6:
//   main.increment()
// Previous write at 0x... by goroutine 5:
//   main.increment()

// Fix con atomic o mutex:
import "sync/atomic"
var atomicCounter int64
atomic.AddInt64(&atomicCounter, 1)  // atomico, thread-safe

Goroutine Sızıntısı: Go'daki En Yaygın Hata

Bir alma kanalında sıkışıp kalan (hiç kimse göndermez) veya almayan bir goroutine İçeriği asla silmemek goroutine sızıntısıdır. Bellek ve CPU biriktirin. Her zaman kullan context uzun işlemler için ve her kanalın bir göndericisinin olduğundan emin olun ve bir alıcı kullanın veya açık temizleme özelliğine sahip ara belleğe alınmış kanalları kullanın.

Sonuçlar

Go'nun eşzamanlılık modeli, arka uç hizmetleri için en etkili modellerden biridir: goroutine Ultra hafif, tasarımı gereği yarış koşullarını engelleyen kanallar, select çoğullama için e context yayılan iptal için. Rakip Go kodlarının çoğu açık mutekslere ihtiyaç duymaz.

Sonraki makale Python'a geçiyor: asyncio.TaskGroup ve yapılandırılmış rekabet Sonunda Go'nun CSP'sine benzer anlamsal güvenliği dünyaya getiren Python 3.11+'da eşzamansız/beklemede.