Kotlin Coroutine — Asenkron ve Paralel Programlama
Tags: asyncawait, kotlin, coroutine, asyncfunctions
Published: 2019-11-02 10:45:54

---
title: Kotlin Coroutine — Asenkron ve Paralel Programlama
published: true
date: 2019-09-18 13:42:18 UTC
tags: asyncawait,kotlin,coroutine,async-functions
canonical_url: 
---

![kotlin coroutine](https://cdn-images-1.medium.com/max/997/1*MeAzvIUCrkcBgGkd2TRBTw.gif)

### Kotlin Coroutine — Asenkron ve Paralel Programlama

Asenkron ve paralel programlama farklı teknoloji ekosistemlerinde farklı anlamlar ifade ettiği için yazıya başlamadan önce bu kavramlara açıklık getirmek istiyorum.

#### Paralel programlama (Parallelism)

Bir görevin paralel olarak işlenebilecek daha küçük alt görevlere ayrılarak birden fazla CPU’da, **threadler** vasıtası ile, paralel olarak yürütülmesi anlamına gelir. Paralel işlemler gereken veri ve kaynak bakımından biribirinden bağımsızdır ve çoğunlukla **senkronizasyona** ihtiyaç duymazlar. Örnek verecek olursak… bir veri kaynağından (DB, filesystem vs.) idendifier’larını bildiğimiz bir veri seti okuyacaksak, bunları belli parçalara bölüp parallel bir şekilde toplayabiliriz.

![](https://cdn-images-1.medium.com/max/286/1*vIjs55BVSRjqvUcgBKVakA.png)

#### Asenkron programlama (Concurrency)

Bir uygulamanın aynı anda (concurrency) birden fazla görevde ilerleme kaydettiği anlamına gelir. Ancak bu işler paralel olarak farklı **thread** lerde **işlenmeyebilir**. Uzun süren IO görevlerinin CPU’yu bloklamamsı amacıyla kullanılır (non-blocking IO). Bir threadde IO işlemi sürerken o threadin bloklanmayarak, başka görevlere tahsis edilmesi ve CPU’nun yüksek utilizasyon ile kullanılması esasına dayalı bir görevdir. Bunu context switch teknikleri kullanarak sağlar. Bu yöntemin uygulanmasındaki zorluk alt görevler arasındaki senkronizasyonu sağlamaktır. Modern programlama dilleri senkronizasyon problemine derleyici seviyesinde çözümler üretmiştir.

**Kotlin** programlama dili bu yöntemi sağlayabilmek için **coroutine** denen bir yapı sunar. Coroutine’ler alt görevleri temsil eder ve **dispatcher** denen bir yapı vasıtası ile görevler arasındaki senkronizasyon sağlanır. . Özetle, dispatcher threadlerin görevler arasında paylaşımı yani context switch işinden sorumludur.

![](https://cdn-images-1.medium.com/max/276/1*uRQkPy0VV4MqX2QfjY_OYw.png)

Yazımın devam eden kısmında **Kotlin** coroutine ile nasıl asenkron programlama yapabileceğimizden bahsedeceğim.

#### Coroutine nedir ?

Öncelikle belirtmem gereken konu, coroutine’lerin **thread olmadığıdır**. Kotlin ekibi coroutine’leri “ **lightweight thread** ” olarak tanımlasa da bu tanım communitynin algısına girebilmek için kullanılan bir benzetmedir. Coroutinei tanımlamak istersek thread tarafından yürütülen görev parçalarıdır diyebiliriz. Başlıktaki gif’den de anlaşılabileceği gibi herhangi bir thread x anında bir coroutine’i alıp işletebilir. Ancak bu threadin ilgili coroutine’i bitene kadar bekleyeceği anlamına gelmez. Bloklayıcı bir bölüme gelindiğinde (suspension point) thread coroutine’i bırakır ve başka bir coroutine’i işletmeye başlayabilir (context switch). Suspend olan görev, işini bitirdiğinde tekrar başka bir thread tarafından işletimine devam edilebilir. Böylelikle thread’ler bloklanmadan verimli bir şekilde kullanılmış olur. Bunu **CoroutineDispatcher** organize eder. CoroutineDispatcher context switching için **suspend** keyword’ü ile etiketlenmiş **özel function’** ları kullanır. Coroutine ile kod örneklerine geçmeden önce suspend functionların iyi anlaşılması gerekmektedir.

![](https://cdn-images-1.medium.com/max/720/1*_YqtHUC9Y84I46ygsFHfnw.gif)


#### Suspend function

Coroutine’lerin belli suspension pointlerde context switche girdiğinden bahsetmiştim. Bu noktaları derleyiciye geliştirici kendi deklare etmek durumundadır. Bu deklarasyonlar ilgili methodu _suspend_ keywordü ile etiketleyerek sağlanır.

{% gist https://gist.github.com/CanerPatir/e94bce5ea3f201470fde3d56a646ee35 %}

Özetle; suspend functionlar, mevcut threadi bloke etmeden coroutinin yürütülmesini askıya alır. Böylelikle thread başka bir corutinenin işletilmesine başlar ve cpu daha verimli bir şekilde kullanılmış olur.

Suspend functionlarla ilgili bir başka bilinmesi gereken konu da sıralı işletildikleridir. Yani dönüş değeri almadan ya da kodun geri kalanını çalıştırmadan önce, çağrılan suspend functionın yürütülmesini beklememiz gerekir. Bu durum asenkron operasyonların Kotlin ile ne denli kolay olduğuna bir kanıt niteliğindedir. Özetle, asenkron kod yazımının, normal yazımdan hiçbir **farkı yoktur**.

{% gist https://gist.github.com/CanerPatir/97187c54a7d5f70efdde134e1b900a0c %}

```
operation1 done after 1 second
operation2 done after 2 seconds and result: hello
```

Suspend functionlar diğer dillerin asenkron yazımdan farklı olarak, geriye özel veri tipleri dönmek zorunda değillerdir.

- Java — Future, Flux, Mono, Single, Observable ve binlercesi :)
- Javascrip t— Promise
- C# — Task

#### Yeni bir coroutine başlatma

Kotlin standart kütüphanesi, coroutine başlatmak için **coroutine builder** denen yapıları barındırır. Coroutine builder, belirli bir suspend fonksiyonu sarmalayan ve işletilmesini sağlayan fonksiyonlardır. Yani, yeni bir coroutine başlatmak için kullanırız. Bunlardan başlıcalarına göz atacak olursak.

#### CoroutineScope.Async

Geriye değer dönen asenkron operasyonların işletilmesi için kullanılır. Beklenen sonucu almak için **Deffered** dönüş tipi kullanır. Deferred, Javadaki _Future_ veya Javascript dilindeki _Promise_ in karşılığıdır diyebiliriz_._ Sonucu beklemek için Deffered sonucun **await** methodunu çağırmak gerkir. Await bir suspend functiondır.

{% gist https://gist.github.com/CanerPatir/3731f1f0791c2982836c5d958a4d996c %}

#### CoroutineScope.Launch

Geriye dönüş tipi gerektirmeyen, arka planda işletilecek operasyonlar için kullanılan builder tipidir. **Fire and forget** mantığı ile çalışabilecek işler için uygundur.

{% gist https://gist.github.com/CanerPatir/7c872e019bd016af604f6b52fa50c645 %}

```
Hello World!
```

#### **Runblocking**

İlgili coroutine tamamlanana kadar, mevcut threadin bloke edilmesini sağlar. Coroutine içerisinde kullanılmamalıdır. Genelde blocking tarzda yazılmış koda coroutine scope bağlamak için ya da test amaçlı kullanılır. Yani normal dünya ile suspending dünya arasında köprü görevi görür. Bütün kod bloğu uçtan uca non-blocking tarzda yazılmışsa kullanımına gerek yoktur.

> Kotlin ekibi coroutine scope içerisinde thread bloklayacak yaklaşımlardan kaçınılmasını önerir. Bu sebeple, runBlocking’in coroutine içerisinde kullanmaktan kaçınmalıyız. Eğer runBlocking kullanılacaksa, hiyerarşide daima en tepede yer almalıdır.

#### Eşzamanlı İşletim

Coroutineler eş zamanlı işletime de olanak sağlar. Aşağıdaki kod örneğinde eş zamanlı ve sıralı işletimin karşılaştırmasını görebilirsiniz.

{% gist https://gist.github.com/CanerPatir/652a70f607ee17b3a467426627d75522 %}

```
operation1: 247 ms
operation2: 5112 ms
```

#### Dispatcher — I/O ve CPU bağımlı işler

Kotlin coroutine altyapısında dispatcher denen nesneler vasıtasıyla senkronizasyon yaptığından bahsetmiştik. Dispatcherların reactive frameworklerdeki (örn: rxJava) **scheduler** nesnesine karşılık geldiğini düşünebiliriz. Corotuineler CPU açısından hiçbir anlam ifade etmeyen basit görev abstractionlarıdır. Dispatcher, coroutin yürütmesinden sorumludur t anında bir coroutine’i alıp threadpoolda bir threade atayarak işletilmesini sağlar ve gerektiğinde coroutine’i suspend ederek threadin başka bir coroutine yürütmesini sağlayabilir. Bu bağlamda, dispatcherlar coroutine’leri CPU için anlamlı hale getirmiş olur diyebiliriz.

Ancak burada dikkat edilmesi gereken nokta ilgili işin networkden ya da filesystemden okuma yazma yapan I/O ağarlıklı bir iş mi yoksa yüksek aritmetik ve mantıksal operasyon barındıran CPU intensive bir iş mi olduğudur. Kotlin coroutine bu tip operasyonları I/O ya da CPU yoğun işlere özelleşmiş thread poollarda ele alır. Kotlin’e ilgili coroutine’in hangi kapsamda ele alıncağının bildirilmesi **yazılım geliştiricinin sorumluluğundadır**. Bunun için Kotlin, _kotlinx.coroutines_ paketi içerisinde aşağıdaki öntanımlı dispatcherları barındırır.

- **Dispatchers.Default** : Herhangi bir dispatcher belirtilmemişse, tüm coroutine üreticileri tarafından kullanılan varsayılan dispatcherdır. CPU yoğun görevlerin yürütülmesi için uygun seçimdir. (RxJava’daki Schedulers.Computation)
- **Dispatchers.IO** : I/O yoğun işlemler için kullanılır. ( RxJava’daki Schedulers.IO)

{% gist https://gist.github.com/CanerPatir/f371568c393fd78a708d7707db63e832 %}

I/O yoğun işe örnek olarak databaseden yaptığımız okuma yazmaları veya http çağrılarını, CPU yoğun işe ise hash operasyonu ya da görsel işlemeyi örnek gösterebiliriz. Eğer kurumsal bir uygulama geliştiriyorsak ağırlıklı olarak databaseden okuma yazma yaparız bu da I/O yoğun bir uygulamamız olduğu anlamına gelir. Yazımın önceki bölümlerinde bir I/O işlemi sürerken, threadin bloke edilmeksizin başka kaynaklara tahsis edilebileceğinden bahsetmiştim. Coroutine ile hedeflenen tam da budur ve bu durum coroutine kullanımının kurumsal uygulamalar için oldukça mantıklı olduğu anlamına gelir.

![](https://cdn-images-1.medium.com/max/556/1*cxTEI5VMoxiIiyImKtWEsQ.png)<figcaption>CPU bloklayan bir IO operasyonu</figcaption>

![](https://cdn-images-1.medium.com/max/512/1*BYcFmqG4CaObLkqyrpXFBw.png)<figcaption>Non-Blocking IO operasyonu</figcaption>

#### Özet

Asenkron işlemler günümüzde çoğu yazılım geliştiricinin bir şekilde içerisinde yer aldığı ya da farkında olmadan yer almak zorunda olduğu bir durumdur. Örneğin, bir web uygulaması geliştiriyorsak concurrency bizim için kritiktir. İstemcilere sağlıklı bir hizmet verebilmek için, uygulamamızın birim zamanda karşılayabildiği istek sayısını ( **throughput** ) en yüksekte tutmamız gerekir. Veya bir client uygulaması geliştiriyorsak, kullanıcı deneyimi açısından, arkaplanda yaptığımız işlemlerin kullanıcının UI üzerinde yaptığı işlemleri bloke etmemesini sağlamamız gerekir. Önceleri bu problemlerin çözümü için multi-threading tekniklerini kullansak da senkronizasyon problemi ve kullanımdaki zorluklar, yazılım endüstrisini bu konuda yeni çözümlerin arayışına itmiştir. Bu bağlamda, java ekosisteminde **rxJava** , Spring’de **Reactor** , c#’da **async/awai** t, Golang’da **goroutine** gibi daha yüksek seviye çözümler yazılım geliştiricilerin hizmetine sunuldu. Bütün bu çözümlerin ortak noktası yazılım geliştiriciyi karmaşık thread operasyonlarından soyutlayarak, önceden optimize edilmiş bir ortam sunmaktır. Kotlin ekibinin sunduğu çözüm ise coroutine olmuştur. Yazımda kotlin coroutine kullanımına ve kavramlara dair genel bilgiler vermek istedim. Kotlin coroutine’leri kullanarak, I/O ağırlıklı bir uygulamanın, aynı kaynak kullanılarak daha fazla istek karşılayabilmesini sağlayabiliriz. Bunu uygulamasını ve karşılaştırmalı yük testlerini, önümüzdeki haftalarda yeni bir medium postu ile paylaşacağım. Yeni yazımda görüşene dek şimdilik hoşçakalın :)

* * *