Bu derste Go dilinde goroutine’ler ve concurrency (eşzamanlılık) kavramlarını öğreneceksiniz. Bu temel ders, goroutine’lerin nasıl çalıştığını ve Go dilindeki concurrency yapılarının nasıl kullanıldığını anlamanızı sağlayacak.
Goroutine, Go dilinde hafif iş parçacıklarıdır. Çok az kaynak kullanarak eşzamanlı olarak çalışabilen küçük görevler olarak düşünülebilir. Bir fonksiyonun goroutine olarak çalıştırılması, bu fonksiyonun ayrı bir iş parçacığı gibi davranmasını sağlar, ancak Go dilinde bu iş parçacıkları daha hafif ve verimlidir.
package main
import (
"fmt"
"time"
)
func printMessage() {
fmt.Println("Goroutine çalışıyor!")
}
func main() {
go printMessage() // Goroutine olarak çalıştırılıyor
time.Sleep(1 * time.Second) // Ana goroutine'nin bitmesini beklemek için
fmt.Println("Main fonksiyonu tamamlandı")
}
Goroutine’ler, bir fonksiyonu eşzamanlı olarak çalıştırmak istediğiniz her durumda kullanılabilir. Bir fonksiyonu goroutine olarak çalıştırmak için go anahtar kelimesini kullanmanız yeterlidir. Bu, fonksiyonun anında yürütülmeye başlamasını sağlar, ancak ana programın yürütülmesiyle aynı anda devam eder.
package main
import (
"fmt"
"time"
)
func countToTen() {
for i := 1; i <= 10; i++ {
fmt.Println(i)
time.Sleep(500 * time.Millisecond)
}
}
func main() {
go countToTen()
fmt.Println("Main fonksiyonu devam ediyor...")
time.Sleep(6 * time.Second)
}
Concurrency, aynı anda birden fazla işlemin yürütülebileceği anlamına gelir. Go dilinde, goroutine’ler sayesinde birden fazla iş aynı anda çalıştırılabilir. Bu, programların daha verimli çalışmasını sağlar.
Paralellik ise, bu eşzamanlı işlerin gerçekten aynı anda fiziksel olarak farklı işlemcilerde çalıştırılmasıdır. Concurrency, paralellik ile aynı şey değildir, ancak paralelliğe olanak tanır.
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Merhaba!")
time.Sleep(1 * time.Second)
}
func sayGoodbye() {
fmt.Println("Hoşça kal!")
time.Sleep(1 * time.Second)
}
func main() {
go sayHello()
go sayGoodbye()
time.Sleep(2 * time.Second)
fmt.Println("Main fonksiyonu tamamlandı")
}
Kanallar (channels), goroutine’ler arasında veri iletmek için kullanılan yapılandırmalardır. Bir goroutine bir kanala veri gönderir, diğer bir goroutine ise bu veriyi alır. Bu, eşzamanlı işlemler arasında güvenli veri iletişimi sağlar.
package main
import (
"fmt"
)
func sendMessage(ch chan string) {
ch <- "Merhaba, Kanal!"
}
func main() {
messageChannel := make(chan string)
go sendMessage(messageChannel)
message := <-messageChannel
fmt.Println(message)
}
Bu örnekte, sendMessage fonksiyonu bir mesajı messageChannel kanalına gönderir. Ana fonksiyon bu mesajı kanaldan alır ve yazdırır. Bu, goroutine’ler arasında veri iletişimini gösteren basit bir örnektir.
select
ifadesi, birden fazla kanal işlemini beklemek ve bu işlemlerden biri gerçekleştiğinde yürütmek için kullanılır. Bu, goroutine’lerin belirli koşullarda nasıl tepki vereceğini kontrol etmenin güçlü bir yoludur.
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "ch1'den mesaj"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "ch2'den mesaj"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(3 * time.Second):
fmt.Println("Timeout gerçekleşti")
}
}
Bu örnekte, select ifadesi, iki kanaldan gelen mesajları bekler ve bu mesajlardan hangisi önce gelirse onu işleyerek ekrana yazdırır. Eğer belirli bir süre içinde mesaj alınmazsa, “Timeout gerçekleşti” mesajı gösterilir.
sync
paketi içinde yer alan WaitGroup
yapısı, goroutine’lerin tamamlanmasını beklemek için kullanılır. Bu yapı, bir veya daha fazla goroutine’nin tamamlanmasını beklemek için kullanılır.
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // WaitGroup sayacını azalt
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // Tüm goroutine'lerin bitmesini bekler
fmt.Println("Tüm işler tamamlandı")
}
Bu örnekte, worker fonksiyonu 5 farklı goroutine olarak çalıştırılır ve wg.Wait() ifadesi, tüm worker goroutine’lerinin tamamlanmasını bekler.
Mutex ve RWMutex, Go dilinde eşzamanlılık sorunlarını çözmek için kullanılan senkronizasyon yapılarıdır. Mutex, bir veriye aynı anda sadece bir goroutine’nin erişmesine izin verirken, RWMutex, bir veriye aynı anda birden fazla okuma işlemine izin verirken yazma işlemlerini sadece tek bir goroutine’nin yapmasını sağlar.
package main
import (
"sync"
"time"
)
func incrementCounter(counter *int, mutex *sync.Mutex) {
mutex.Lock()
*counter++
mutex.Unlock()
}
func failIncrementCounter(counter *int) {
*counter++
}
func main() {
var withoutMutexCounter int = 0
var withoutMutexCounter2 int = 0
var withoutMutexCounter3 int = 0
var withoutMutexCounter4 int = 0
var withoutMutexCounter5 int = 0
var withMutexCounter int
var mutex sync.Mutex
for i := 0; i < 1000; i++ {
go incrementCounter(&withMutexCounter, &mutex)
go failIncrementCounter(&withoutMutexCounter)
go failIncrementCounter(&withoutMutexCounter2)
go failIncrementCounter(&withoutMutexCounter3)
go failIncrementCounter(&withoutMutexCounter4)
go failIncrementCounter(&withoutMutexCounter5)
}
time.Sleep(5 * time.Second)
mutex.Lock()
println("With Mutex Counter:", withMutexCounter)
mutex.Unlock()
println("Without Mutex Counter:", withoutMutexCounter)
println("Without Mutex Counter2:", withoutMutexCounter2)
println("Without Mutex Counter3:", withoutMutexCounter3)
println("Without Mutex Counter4:", withoutMutexCounter4)
println("Without Mutex Counter5:", withoutMutexCounter5)
}
>> go run main.go
With Mutex Counter: 1000
Without Mutex Counter: 876
Without Mutex Counter2: 987
Without Mutex Counter3: 564
Without Mutex Counter4: 923
Without Mutex Counter5: 997
Bu örnekte, sync.Mutex
yapısı kullanılarak incrementCounter
fonksiyonu ile güvenli bir şekilde bir sayacı artırırken, failIncrementCounter
fonksiyonu ile aynı işlemi güvenli olmayan bir şekilde yapmaktadır. Bu durumda, güvenli olmayan şekilde artırılan sayacıların değerlerinde hatalar oluşmaktadır.