Neler yeni

Yazılım Forum

Tüm özelliklerimize erişmek için şimdi bize katılın. Kayıt olduktan ve giriş yaptıktan sonra konu oluşturabilecek, mevcut konulara yanıt gönderebilecek, itibar kazanabilecek, özel mesajlaşmaya erişebilecek ve çok daha fazlasını yapabileceksiniz! Bu hizmetlerimiz ise tamamen ücretsiz ve kurallara uyulduğu sürece sınırsızdır, o zaman ne bekliyorsunuz? Hadi, sizde aramıza katılın!

Go Dilinde Eşzamanlı Programlama: Goroutine'ler, Kanallar ve Select İfadeleri

Go Dilinde Eşzamanlılık: Goroutine'ler ve Kanalların Gücü

Günümüzün modern yazılım geliştirme dünyasında, uygulamaların daha hızlı, daha ölçeklenebilir ve daha duyarlı olması beklenmektedir. Bu beklentileri karşılamanın anahtarlarından biri de eşzamanlı programlamadır. Eşzamanlılık (Concurrency), birden fazla görevin aynı anda ilerleyebilmesi yeteneğidir; paralelizm (Parallelism) ise bu görevlerin gerçekten aynı anda yürütülmesidir. Go programlama dili, eşzamanlılığı dilin çekirdeğine entegre ederek, geliştiricilerin karmaşık paralel sistemler inşa etmesini olağanüstü derecede kolaylaştırmıştır.

Go'nun Eşzamanlılık Felsefesi: CSP Modeli

Go'nun eşzamanlılık modeli, Tony Hoare tarafından geliştirilen İletişim Kurulabilen Sıralı Süreçler (Communicating Sequential Processes - CSP) teorisine dayanmaktadır. Bu modelde, bağımsız çalışan süreçler (Go'da goroutine'ler) mesajlaşma yoluyla (Go'da kanallar aracılığıyla) birbirleriyle iletişim kurar. Go'nun ünlü mottosu:
Do not communicate by sharing memory; instead, share memory by communicating.
(Bellek paylaşarak iletişim kurmayın; bunun yerine, iletişim kurarak belleği paylaşın.) Bu felsefe, geleneksel paylaşılan bellek ve kilit mekanizmalarının yol açtığı veri yarışları (data races) ve kilitlenmeler (deadlocks) gibi sorunları büyük ölçüde azaltmayı hedefler.

Goroutine'ler: Go'nun Hafif İş Parçacıkları

concurrency_diagram.png

Go dilinde eşzamanlılığın temel yapı taşı goroutine'lerdir. Goroutine'ler, işletim sistemi iş parçacıklarından (thread) çok daha hafiftir ve binlercesi hatta milyonlarcası aynı anda çalışabilir. Bir goroutine, basit bir fonksiyon çağrısının başına `go` anahtar kelimesi getirilerek oluşturulur. Go çalışma zamanı (runtime), goroutine'leri işletim sistemi iş parçacıkları üzerinde verimli bir şekilde zamanlar ve yönetir.

Kod:
package main

import (
    "fmt"
    "time"
)

func merhaba(isim string) {
    for i := 0; i < 3; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("Merhaba, %s! (%d)\n", isim, i)
    }
}

func main() {
    go merhaba("Go") // Goroutine'i başlat
    fmt.Println("Main goroutine çalışıyor...")
    time.Sleep(500 * time.Millisecond) // Goroutine'in bitmesini beklemek için biraz bekleyelim
    fmt.Println("Main goroutine bitti.")
}
Yukarıdaki örnekte `merhaba("Go")` fonksiyonu, `go` anahtar kelimesiyle ayrı bir goroutine olarak çalıştırılırken, `main` fonksiyonu ana goroutine'de çalışmaya devam eder. `main` fonksiyonu bitmeden diğer goroutine'lerin işini tamamlaması için bir bekleme süresi ekledik. Aksi takdirde, `main` goroutine'i bittiğinde program sonlanır ve diğer goroutine'ler tamamlanmayabilir.

Kanallar: Goroutine'ler Arası Güvenli İletişim

Goroutine'ler bağımsız çalışır, ancak genellikle birbirleriyle iletişim kurmaları veya veri paylaşmaları gerekir. Go'da bu iletişim, kanallar (channels) aracılığıyla güvenli ve senkronize bir şekilde gerçekleşir. Kanallar, belirli bir veri tipinde değer gönderip almak için kullanılan boru hatları gibidir.

Kanallar iki ana kategoriye ayrılır:
  • Arabelleklenmemiş (Unbuffered) Kanallar: Bir değer gönderildiğinde, alıcı taraf o değeri alana kadar gönderici engellenir. Benzer şekilde, bir değer alınmaya çalışıldığında, gönderici o değeri gönderene kadar alıcı engellenir. Bu, senkronize bir iletişim sağlar.
  • Arabellekli (Buffered) Kanallar: Belirli bir boyuta kadar değerleri depolayabilirler. Arabellek dolana kadar gönderici engellenmez. Arabellek boşalana kadar da alıcı engellenmez. Bu, bir miktar asenkron iletişim sağlar.

Kanal oluşturmak için `make(chan T)` (arabelleklenmemiş) veya `make(chan T, kapasite)` (arabellekli) kullanılır. Veri göndermek için `kanal <- değer`, veri almak için `değer := <-kanal` veya `<-kanal` kullanılır.

Kod:
package main

import "fmt"
import "time"

func topla(sayılar []int, c chan int) {
    toplam := 0
    for _, s := range sayılar {
        toplam += s
    }
    c <- toplam // Sonucu kanala gönder
}

func main() {
    sayılar := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    c := make(chan int) // Arabelleklenmemiş kanal

    go topla(sayılar[:len(sayılar)/2], c) // İlk yarısı için goroutine
    go topla(sayılar[len(sayılar)/2:], c) // İkinci yarısı için goroutine

    x, y := <-c, <-c // Kanallardan sonuçları al
    fmt.Println("Yarım toplamlar:", x, y)
    fmt.Println("Toplam:", x+y)

    // Kanal kapatma ve range ile okuma
    cBuff := make(chan string, 3) // 3 kapasiteli arabellekli kanal
    cBuff <- "Birinci"
    cBuff <- "İkinci"
    cBuff <- "Üçüncü"
    close(cBuff) // Kanalı kapatmak, daha fazla değer gönderilemeyeceği anlamına gelir.

    fmt.Println("\nArabellekli kanaldan okuma:")
    for mesaj := range cBuff {
        fmt.Println(mesaj)
    }

    // Kapalı kanaldan okuma
    deger, ok := <-cBuff
    fmt.Printf("Kapalı kanaldan okuma denemesi: %q, açık mı? %v\n", deger, ok) // ok false döner
}
Bu örnekte, `topla` fonksiyonu iki farklı goroutine olarak çalışarak diziyi ikiye bölüp kendi toplamlarını hesaplar ve sonuçları ana goroutine'e bir kanal aracılığıyla geri gönderir. `main` fonksiyonu, bu sonuçları kanaldan okuyana kadar bekleyecektir. Ayrıca, arabellekli kanal kullanımı ve bir kanalın `close` edilmesi ile `range` döngüsüyle okunması gösterilmiştir. Bir kanal kapatıldıktan sonra ondan okuma denendiğinde `ok` değeri `false` döner.

`select` İfadesi: Çoklu Kanal İşleme

Select ifadesi, birden fazla kanaldan aynı anda veri almayı veya veri göndermeyi beklemek için kullanılır. Bir kanal operasyonu hazır olduğunda, `select` bloğu içindeki ilgili `case` çalıştırılır. Eğer birden fazla `case` hazırsa, Go çalışma zamanı rastgele birini seçer. `default` durumu, hiçbir kanal operasyonu hazır değilse hemen çalıştırılmak üzere kullanılabilir. Bu, kilitlenmeleri önlemek ve non-blocking (engellemeyen) iletişim sağlamak için kullanışlıdır.

Kod:
package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "Birinci mesaj"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "İkinci mesaj"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("Alındı:", msg1)
        case msg2 := <-c2:
            fmt.Println("Alındı:", msg2)
        case <-time.After(1500 * time.Millisecond): // 1.5 saniye sonra zaman aşımı
            fmt.Println("Zaman aşımı!")
        // default:
        //     fmt.Println("Hiçbir kanal hazır değil, devam ediliyor.")
        }
    }
}
Yukarıdaki `select` örneği, iki farklı kanaldan mesaj beklerken aynı zamanda bir zaman aşımı durumunu da ele alır. Eğer 1.5 saniye içinde hiçbir mesaj gelmezse, "Zaman aşımı!" mesajı yazdırılır. `default` bloğu yorum satırı olarak bırakılmıştır; eğer aktif olsaydı, hiçbir kanal işlemi anında hazır olmadığında `select` bloğu hemen `default` durumunu çalıştırır ve program engellenmezdi.

Eşzamanlılık İçin Diğer Araçlar

Go, eşzamanlı uygulamalar geliştirmek için sadece goroutine ve kanallarla sınırlı değildir; `sync` paketi gibi ek araçlar da sunar.

sync.WaitGroup: Bir grup goroutine'in tamamlanmasını beklemek için kullanılır. `Add` ile beklenen goroutine sayısını artırır, `Done` ile bir goroutine bittiğinde sayacı azaltır ve `Wait` ile sayacın sıfır olmasını bekler.
Kod:
package main

import (
    "fmt"
    "sync"
    "time"
)

func işYap(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Fonksiyon bitince Done çağır
    fmt.Printf("Goroutine %d işe başladı.\n", id)
    time.Sleep(time.Duration(id) * 200 * time.Millisecond)
    fmt.Printf("Goroutine %d işi bitirdi.\n", id)
}

func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 3; i++ {
        wg.Add(1) // Bir goroutine eklendi
        go işYap(i, &wg)
    }
    wg.Wait() // Tüm goroutine'lerin bitmesini bekle
    fmt.Println("Tüm goroutine'ler tamamlandı.")
}

sync.Mutex ve RWMutex: Geleneksel paylaşılan bellek erişimini senkronize etmek için kilit mekanizmaları sunar. `Mutex` karşılıklı dışlama (mutual exclusion) sağlarken, `RWMutex` okuma-yazma kilitleri sunar (birden fazla okuyucuya aynı anda izin verir, ancak yazıcılar için tek erişim sağlar). Ancak Go felsefesi gereği, mümkün olduğunda kanalların kullanılması tavsiye edilir.

Dikkat Edilmesi Gerekenler ve En İyi Uygulamalar

Eşzamanlı programlama güçlü olsa da, dikkatli kullanılmadığında hatalara yol açabilir:
  • Deadlock (Kilitlenme): İki veya daha fazla goroutine'in birbirini sonsuza kadar beklemesi durumudur. Genellikle yanlış kanal kullanımı veya kilit mekanizmalarından kaynaklanır.
  • Goroutine Bellek Sızıntıları: Bir goroutine'in asla tamamlanmaması ve kaynaklarını serbest bırakmaması durumudur. Kanal gönderme/alma işlemlerinin hiç gerçekleşmemesi veya `context` ile iptal mekanizmalarının kullanılmaması ile oluşabilir.
  • Veri Yarışları (Data Races): Birden fazla goroutine'in aynı anda paylaşılan bir belleğe erişmeye çalışması ve en az birinin yazma işlemi yapması durumudur. Kanal kullanımı ile çoğu engellenir, ancak `sync.Mutex` kullanırken dikkatli olunmalıdır. Go'nun `go run -race` komutu bu tür sorunları tespit etmeye yardımcı olur.
  • Context Paketi: Büyük eşzamanlı uygulamalarda goroutine'ler arası iptal sinyalleri göndermek, zaman aşımı belirlemek ve isteğe özgü değerleri taşımak için `context` paketi kullanılır. Bu paket, uzun süreli operasyonları güvenli bir şekilde iptal etmek için kritik öneme sahiptir.

Kod:
package main

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

func uzunSurenIs(ctx context.Context, id int) {
    select {
    case <-time.After(3 * time.Second):
        fmt.Printf("İş %d tamamlandı.\n", id)
    case <-ctx.Done():
        fmt.Printf("İş %d iptal edildi: %v\n", id, ctx.Err())
    }
}

func main() {
    // 2 saniye sonra iptal olacak bir context
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel() // context'i serbest bırakmayı unutma

    fmt.Println("İşlem başlıyor...")
    go uzunSurenIs(ctx, 1) // Goroutine'i başlat

    time.Sleep(3 * time.Second) // Ana goroutine'in beklemesi
    fmt.Println("Main goroutine bitti.")
}
Yukarıdaki örnekte, bir goroutine 3 saniye sürmesi gereken bir iş yaparken, ana goroutine 2 saniye sonra `context` aracılığıyla iptal sinyali gönderir. Bu, uzun süren işin zaman aşımına uğramasını veya dışarıdan iptal edilmesini sağlar.

Sonuç

Go dili, goroutine'leri ve kanalları dilin çekirdeğine entegre ederek, eşzamanlı programlamayı hem kolay hem de güvenli hale getirmiştir. Bu güçlü özellikler sayesinde, geliştiriciler karmaşık senkronizasyon sorunlarıyla boğuşmak yerine, iş mantığına odaklanabilir ve yüksek performanslı, ölçeklenebilir ve hata toleranslı uygulamalar oluşturabilirler. Go'nun eşzamanlılık modeli, modern çok çekirdekli sistemlerin gücünden tam olarak yararlanmak için ideal bir çerçeve sunar. Bu konuyu daha derinlemesine incelemek için Go'nun resmi dokümantasyonunu ziyaret etmeniz şiddetle tavsiye edilir. Go ile eşzamanlılık, karmaşık sistemleri yönetilebilir ve anlaşılır kılmanın yeni bir yolunu sunar.
 
shape1
shape2
shape3
shape4
shape5
shape6
Üst

Bu web sitenin performansı Hazal Host tarafından sağlanmaktadır.

YazilimForum.com.tr internet sitesi, 5651 sayılı Kanun’un 2. maddesinin 1. fıkrasının (m) bendi ve aynı Kanun’un 5. maddesi kapsamında Yer Sağlayıcı konumundadır. Sitede yer alan içerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır.

YazilimForum.com.tr, kullanıcılar tarafından paylaşılan içeriklerin doğruluğunu, güncelliğini veya hukuka uygunluğunu garanti etmez ve içeriklerin kontrolü veya araştırılması ile yükümlü değildir. Kullanıcılar, paylaştıkları içeriklerden tamamen kendileri sorumludur.

Hukuka aykırı içerikleri fark ettiğinizde lütfen bize bildirin: lydexcoding@gmail.com

Sitemiz, kullanıcıların paylaştığı içerik ve bilgileri 6698 sayılı KVKK kapsamında işlemektedir. Kullanıcılar, kişisel verileriyle ilgili haklarını KVKK Politikası sayfasından inceleyebilir.

Sitede yer alan reklamlar veya üçüncü taraf bağlantılar için YazilimForum.com.tr herhangi bir sorumluluk kabul etmez.

Sitemizi kullanarak Forum Kuralları’nı kabul etmiş sayılırsınız.

DMCA.com Protection Status Copyrighted.com Registered & Protected