Goroutine 101

Aykut İsmet Aslantaş
GoTurkiye
Published in
4 min readJul 11, 2023

Goroutine nedir ?

Go yazarken işlemleri veya fonksiyonları eşzamanlı olarak çalıştırmak istediğimizde devreye Goroutine’leri sokarız. Gorouteine’lerin kullanılmasında ki en önemli faktör işlemcileri etkin bir şekilde kullanarak işlemci kaynaklarının verimli bir şekilde kullanılmasını sağlamasıdır.

Goroutine’lerin teorik tanımını yaptıktan sonra kod üzerinden anlatmaya geçebiliriz. İlk olarak Main.go dosyamızı oluşturalım, ardından ekrana “Hello World” ve “Merhaba dünya” yazan 2 adet fonksiyon tanımlayalım ve main() fonksiyonu içinde çağıralım

Gayet basit olan kodumuzu go run main.go komutunu kullanarak çalıştırdığımızda ekranda ne bekleriz ? Elbette “Hello World” ve “Merhaba Dünya” yazmasını. Şimdi ise “çok” radikal bir değişiklik yapıp fonksiyonlarımızı çağırırken başına go yazalım ve tekrardan çalıştıralım

Ve evet bize hiç bir sonuç dönmedi, peki neden ?

2 fonksiyonumuzun başına da go ifadesini eklediğimiz de fonksiyonlarımız Goroutein’e atanmış oluyor. Goroutine’lerin çalışma süreçleri ve sonuçları belirsizdir. Bu nedenle, üstte yazdığımız kodda Goroutine’ler hemen başlatılır ve ana program sonlanırken onların çalışması tamamlanmamış olabilir. Kısacası Goroutine’ler programın işlevinden bağımsız olarak çalıştıkları için ana program beklemeksizin devam eder ve program sonlanır.

dipnot olarak eklemek gerekirse main() fonksiyonuda başlı başına bir Goroutine’dir

Bunu önlemek için aklımıza ilk olarak sleep komutunu ekleyerek programa tüm Goroutine’leri tamamlaması için zaman tanımak gelebilir, bunun için sleep’e verdiğimiz 1 saniye fazlasıyla yeter


go helloWorld()
go merhabaDunya()
time.Sleep(1 * time.Second)

sleep komutunu ekledikten sonra beklediğimiz gibi çıktımız karşımızda

Ama programımızı art arda çalıştırınca çok sansasyonel (aslında hemen üstte bahsetmiştim) bir şey farkediyoruz. Bazen önce “Hello World” yazılmış bazen de “Merhaba Dünya” bunun temel nedeni hangi Goroutine’nin önce çalışacağı tamamen rastgele, birbirinden bağımsız olarak çalıştıkları için işlemleri eşzamanlı olarak yürütebilirler.

Bu, programın daha hızlı çalışmasını sağlar ve işlemlerin birbirini beklemesine gerek kalmadan aynı anda gerçekleştirilmesine imkan tanır

arada 1 kez de olsa önce “Merhaba Dünya” yazılmış

Ama çoğu konuda olduğu gibi burada da ilk akla gelen fikir olan sleep() kullanmak pek mantıklı değil çünkü sleep() fonksiyonunda belirtilen süre boyunca programın beklemesini sağlar. Ancak bu süre boyunca program pasif durumda olur ve işlemci kaynakları boşa harcanır. Bu nedenle, genellikle time.sleep() kullanmak yerine senkronizasyon mekanizmaları tercih edilir.

sync Paketi ve sync.WaitGroup

Bu senkronizasyon mekanizmalarının başında sync.WaitGroup yapısı gelir, sync.WaitGroup kullanarak Goroutine'lerin tamamlanmasını bekleyebilir ve programın eşzamanlı çalışmasını kontrol edebiliriz.

İlk olarak sync.WaitGroup struct’ını daha rahat kullanmak için wg değişkenine tanımlayalım, daha sonra wg değişkeni üzerinden eklediğimiz Add(2) fonksiyonu sayesinde programımıza 2 adet Goroutine tanımlayalım.

var wg sync.WaitGroup
wg.Add(2)

Artık programımız 2 adet Goroutine’den haberdar olduğu için biraz üstte yaşadığımız sorun olan programı başlatınca ekranda hiç bir çıktı olmaması söz konusu değil. Ama tabii ki programa bak bizim 2 Goroutine’imiz var yalvarırım onları çalıştırmadan kapanma demek yetmez bir de oluşturduğumuz Goroutine’leri yönetmek lazım.

Yönetmeye başlarken ilk olarak fonksiyonlarımıza wg değişkenini &wg şeklinde parametre olarak belirtelim, burada kullandığımız & (pointer) ile wg değişkenin hafızada tutulduğu adresi alıyoruz ve yolluyoruz, böylece tüm wg struct’ını yollamak yerine sadece adresini yolluyoruz.

Pointerlar hakkında daha fazla bilgi için : https://www.golang-book.com/books/intro/8

Hemen ardından eklediğimiz wg.Wait() komutu ile de tanımladığımız Goroutine’ler tamamanana kadar programı kapatma “bekle” diyoruz.

go helloWorld(&wg)
go merhabaDunya(&wg)
wg.Wait()

Ve tabii ki yolladığımız wg pointerlarını fonksiyonlarımızın parametre kısmından alalım

func helloWorld(wg *sync.WaitGroup) {
fmt.Println("Hello World")
}

Şimdi de alacağımız hatadan habersiz masum bir şekilde programımızı çalıştıralım. Tam da beklediğimiz gibi hatamızı aldık, hatamızda yakındığı şey başlattığımız Goroutine’ler bitene kadar wg.Wait() ile beklemesini söylediğimiz halde işimiz bitince bitirdiğimizi söylemememiz

Bu hatanın önüne geçmek için fonksiyonlarımızın içinde wg.Done() ile Goroutine’lerimizin bittiğini belirtelim, en başa koyduğumuz defer ile de wg.Done() işlemi bitene kadar diğer işlevlerin çalışmasını engelliyoruz.
defer : https://go.dev/tour/flowcontrol/12

Artık kodumuz istediğimiz gibi çalışıyor

Goroutine’lerin etkisi bu tarz basit projeler de pek hissedilmez fakat iş büyük projelere gelince Go’nun ve Goroutine’lerin etkisi kendini fazlasıyla hissettirir

Başka yazılarda görüşmek üzere, Go ile kalın 🐝

--

--

Aykut İsmet Aslantaş
GoTurkiye

Marmara Üniversitesi - Elektrik ve Elektronik Mühendisliği