Concurrent Programming in Golang: Understanding Mutex
Golang, eşzamanlı (concurrent) programlamaya odaklanan bir dil olarak bilinir. Ancak, go routinlerdeki (goroutines) değişkenler arasında paylaşımın düzgün bir şekilde kontrol edilmesi önemlidir. Bu makalede, sync
paketinin Mutex
yapısını kullanarak race condition'ların nasıl ele alınacağını anlayacağız.
Race Condition Nedir?
Race condition, birden fazla go rutini aynı değişkeni değiştirmeye çalıştığında ortaya çıkan durumdur. Bu durum, beklenmeyen sonuçlara, hatalara ve uygulama kararsızlığına yol açabilir. Şöyle ki aynı anda hem veriyi değiştirdiğinizi hem de okumaya çalıştığınız düşünün?
Örnek:
Kod üzerinden gidecek olursak eğer, n değişkeni üzerinde işlem yapan 2 adet goroutinlerimiz mevcut. Bir tanesi değeri güncelliyor iken bir tanesi ise değerin durmuna göre bir çıktı veriyor. Verilmiş olan n değerine göre çıktının sonucunu hepimiz “Çift : 10” olacak şekilde bekleriz. Ama işler biraz farklı ilerliyor…
package main
import (
"fmt"
"time"
)
func CiftMi(n int) bool {
return n%2 == 0
}
func main() {
n := 10
go func() {
is_even := CiftMi(n)
time.Sleep(5 * time.Millisecond)
if is_even {
fmt.Printf("Çift: %d", n)
} else {
fmt.Printf("Tek : %d", n)
}
}()
go func() {
n++
}()
time.Sleep(time.Second)
}
Bunun sebebi tam olarak şu yüzden kaynaklanıyor. Siz bir değişken tanımlıyorsunuz ve o değişken 10 değerini atıyorsunuz. Sonrasında satır satır kodum çalışsın bana çıktı versin diyorsunuz. Ama Concurrent programlamada işlemi devreye alıp bir sonraki işlemi de götürüyor yanında. Bu sebeple ilk goroutin değer kontrolünü 10 üzerinden yapıyor ama bu sırada 2. goroutin değeri arttırmış oluyor ve 1. goroutin ekrana değeri basana kadar değer değişmiş oluyor ve ekrana basarkenki okudğu değer 11 olmuş oluyor. Bu sebeple tutarsız, yanlış sonuç veren bir kod elde etmiş oluyoruz. Peki bu durumun çözümü ne?
Mutex Kullanımı
Mutex (Mutex = Mutual Exclusion), bir anda sadece bir go rutininin belirli bir kod bloğuna erişmesine izin veren bir kilit mekanizmasıdır. İşte bir örnek:
package main
import (
"fmt"
"sync"
"time"
)
func CiftMi(n int) bool {
return n%2 == 0
}
func main() {
n := 10
var m sync.Mutex
go func() {
m.Lock()
defer m.Unlock()
is_even := CiftMi(n)
time.Sleep(5 * time.Millisecond)
if is_even {
fmt.Printf("Çift: %d", n)
} else {
fmt.Printf("Tek : %d", n)
}
}()
go func() {
m.Lock()
n++
m.Unlock()
}()
time.Sleep(time.Second)
}
Bu örnekte, sync.Mutex
kullanarak counter
değişkenini koruduk. Lock
ve Unlock
yöntemleri arasındaki kritik bölge, aynı anda sadece bir go rutini tarafından erişilebilir.
Sonuç
Golang’da Mutex kullanarak, eşzamanlı programlamanın getirdiği riskleri minimize edebilir ve uygulamalarınızın daha güvenilir olmasını sağlayabiliriz. Bu mekanizmaları doğru bir şekilde kullanmak, race condition’ları önlemenin ve kodunuzu daha güvenli hale getirmenin önemli bir adımıdır.
Sosyal Medya ve Bağlantılar:
- GitHub: MuharremCandan
- LinkedIn: Muharrem Candan
- Twitter: @mhrrm_cndn