# Concurrency

Go'nun 21. yüzyılın C'si olduğu söyleniyor. Bence bunun iki nedeni var. Birincisi, Go basit bir dil. İkincisi, eşzamanlılık günümüz dünyasında çok popüler bir konu ve Go bu özelliği dil seviyesinde destekliyor.

## goroutine

goroutine'ler ve eşzamanlılık Go'nun çekirdek tasarımında yer almaktadır. Thread'lere benzerler ancak farklı çalışırlar. Go ayrıca goroutinlerinizde bellek paylaşımı için tam destek sağlar. Bir goroutine genellikle 4~5 KB yığın bellek kullanır. Bu nedenle, tek bir bilgisayarda binlerce goroutine çalıştırmak zor değildir. Bir goroutine, sistem iş parçacıklarından daha hafif, daha verimli ve daha kullanışlıdır.

goroutine'ler Go'da çalışma zamanında iş parçacığı yöneticisi üzerinde çalışır. Temel düzeyde bir fonksiyon olan yeni bir goroutine oluşturmak için `go` anahtar sözcüğünü kullanırız ( ***main() bir goroutine'dir*** ).
```Go
go hello(a, b, c)
```
Bir örnek görelim.
```Go
package main

import (
	"fmt"
	"runtime"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		runtime.Gosched()
		fmt.Println(s)
	}
}

func main() {
	go say("dünya") // yeni bir goroutine oluşturun
	say("merhaba")    // mevcut goroutine
}

```
Çıktı：
```
	merhaba
	dünya
	merhaba
	dünya
	merhaba
	dünya
	merhaba
	dünya
	merhaba
```
Go'da `go` anahtar kelimesini kullanarak eşzamanlılığı kullanmanın çok kolay olduğunu görüyoruz. Yukarıdaki örnekte, bu iki goroutin bir miktar bellek paylaşmaktadır, ancak tasarım tarifini takip etmemiz daha iyi olacaktır: İletişim kurmak için paylaşılan verileri kullanmayın, veri paylaşmak için iletişimi kullanın.

`runtime.Gosched()`, CPU'nun diğer goroutinleri yürütmesine ve bir noktada geri gelmesine izin vermek anlamına gelir.

Go 1.5'te, çalışma zamanı artık `GOMAXPROCS` tarafından tanımlanan aynı anda çalışacak varsayılan iş parçacığı sayısını CPU'daki mevcut çekirdek sayısına göre ayarlamaktadır.

Go 1.5'ten önce, zamanlayıcı tüm goroutinleri çalıştırmak için yalnızca bir iş parçacığı kullanır, bu da yalnızca eşzamanlılığı uyguladığı anlamına gelir. Paralel işlemeden yararlanmak için daha fazla CPU çekirdeği kullanmak istiyorsanız, kullanmak istediğiniz çekirdek sayısını ayarlamak için runtime.`GOMAXPROCS(n)` komutunu çağırmanız gerekir. Eğer `n<1` ise, hiçbir şey değişmez.

## channels

goroutinler aynı bellek adres alanında çalışır, bu nedenle paylaşılan belleğe erişmek istediğinizde senkronizasyonu sürdürmeniz gerekir. Farklı goroutinler arasında nasıl iletişim kurarsınız? Go, `channel` adı verilen çok iyi bir iletişim mekanizması kullanır. Bir `channel` Unix kabuklarındaki iki yönlü boru hattı gibidir: veri göndermek veya almak için `channel` kullanın. Kanallarda kullanılabilecek tek veri tipi `channel` tipi ve `chan` anahtar kelimesidir. Yeni bir `kanal` oluşturmak için `make` kullanmanız gerektiğini unutmayın.
```Go
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
```
kanalı veri göndermek veya almak için `<-` operatörünü kullanır.
```Go
ch <- v    // v'yi kanal ch'ye gönderin.
v := <-ch  // ch'den veri alır ve v'ye atar
```
Daha fazla örnek görelim.
```Go
package main

import "fmt"

func sum(a []int, c chan int) {
	total := 0
	for _, v := range a {
		total += v
	}
	c <- total // toplamı c'ye gönder
}

func main() {
	a := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(a[:len(a)/2], c)
	go sum(a[len(a)/2:], c)
	x, y := <-c, <-c // toplamı c'den al

	fmt.Println(x, y, x+y)
}

```
Kanallarda veri gönderme ve alma varsayılan olarak bloklanır, bu nedenle senkron goroutinleri kullanmak çok daha kolaydır. Bloktan kastım, bir goroutinin boş bir kanaldan veri alırken, yani (`value := <-ch`), diğer goroutinler bu kanala veri gönderene kadar devam etmeyeceğidir. Öte yandan, goroutine bir kanala gönderdiği veri, yani (`ch<-5`), alınana kadar devam etmeyecektir.

## Buffered channels

Yukarıda tamponlanmamış kanalları tanıttım. Go aynı zamanda tek bir elemandan daha fazlasını saklayabilen tamponlu kanallara da sahiptir. Örneğin, `ch := make(chan bool, 4)`, burada 4 boolean elemanı saklayabilen bir kanal oluşturuyoruz. Yani bu kanalın içine bloklama olmadan 4 eleman gönderebiliyoruz, ancak beşinci bir eleman göndermeye çalıştığınızda ve hiçbir goroutine bunu almadığında goroutine bloklanacaktır.
```Go
ch := make(chan type, n)

n == 0 ! non-buffer（block）
n > 0 ! buffer（non-block until n elements in the channel）
```
Aşağıdaki kodu bilgisayarınızda deneyebilir ve bazı değerleri değiştirebilirsiniz.
```Go
package main

import "fmt"

func main() {
	c := make(chan int, 2) // 2'yi 1 olarak değiştirirseniz çalışma zamanı hatası olur, ancak 3 iyidir
	c <- 1
	c <- 2
	fmt.Println(<-c)
	fmt.Println(<-c)
}

```
## Range and Close

Dilim ve eşlemede olduğu gibi tampon kanallar üzerinde çalışmak için aralığı kullanabiliriz.
```Go
package main

import (
	"fmt"
)

func fibonacci(n int, c chan int) {
	x, y := 1, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

```
`for i := range c` kanal kapatılana kadar kanaldan veri okumayı durdurmayacaktır. Yukarıdaki örnekte kanalı kapatmak için `close` anahtar sözcüğünü kullanıyoruz. Kapalı bir kanalda veri göndermek veya almak imkansızdır; bir kanalın kapalı olup olmadığını test etmek için `v, ok := <-ch` kullanabilirsiniz. Eğer `ok` false döndürürse, bu kanalda veri olmadığı ve kanalın kapalı olduğu anlamına gelir.

Kanalları her zaman tüketicilerde değil üreticilerde kapatmayı unutmayın, aksi takdirde panik durumuna geçmek çok kolaydır.

Hatırlamanız gereken bir başka şey de kanalların dosyalar gibi olmadığıdır. Kanalın tamamen yararsız olduğundan emin değilseniz veya aralık döngülerinden çıkmak istemiyorsanız, bunları sık sık kapatmanız gerekmez.

## Select

Yukarıdaki örneklerde sadece bir kanal kullandık, ancak birden fazla kanalla nasıl başa çıkabiliriz? Go, birçok kanalı dinlemek için `select` adlı bir anahtar kelimeye sahiptir.

`select` varsayılan olarak engelleyicidir ve yalnızca kanallardan birinde gönderilecek veya alınacak veri olduğunda çalışmaya devam eder. Aynı anda birden fazla kanal kullanıma hazırsa, select hangisinin çalıştırılacağını rastgele seçer.
```Go
package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

```
`select`in de tıpkı `switch` gibi bir `default` durumu vardır. Tüm kanallar kullanıma hazır olmadığında, varsayılan durumu çalıştırır (artık kanalı beklemez).
```Go
select {
case i := <-c:
// use i
default:
// executes here when c is blocked
}
```
## Timeout

Bazen bir goroutine bloke olur. Tüm programın bloke olmasını önlemek için bunu nasıl önleyebiliriz? Çok basit, select içinde bir zaman aşımı ayarlayabiliriz.
```Go
func main() {
	c := make(chan int)
	o := make(chan bool)
	go func() {
		for {
			select {
			case v := <-c:
				println(v)
			case <-time.After(5 * time.Second):
				println("timeout")
				o <- true
				break
			}
		}
	}()
	<-o
}

```
## Runtime goroutine

`runtime` paketi goroutine'lerle çalışmak için bazı fonksiyonlara sahiptir.

- `runtime.Goexit()`

	Geçerli goroutine'den çıkar, ancak ertelenen işlevler her zamanki gibi yürütülür.

- `runtime.Gosched()`

	Zamanlayıcının diğer goroutinleri yürütmesine ve bir noktada geri gelmesine izin verir.

- `runtime.NumCPU() int`

	CPU çekirdeği sayısını döndürür

- `runtime.NumGoroutine() int`

	Goroutin sayısını döndürür

- `runtime.GOMAXPROCS(n int) int`

	Kaç CPU çekirdeği kullanmak istediğinizi ayarlar

## Links

- [Directory](preface.md)
- Önceki bölüm: [interface](02.6.md)
- Sonraki bölüm: [Summary](02.8.md)
