Coroutineler Coroutineler neeeyymis bu Coroutineler (*Kotlin)

Ahmet ÇELİK
6 min readFeb 9, 2019

--

Hikaye

Yukarıdaki sorunun cevabını hemen vermek gerekirse. Coroutineler lightweight threadlerdir.

Şimdi aslında değildir ama insanların aşına olduğu bir şeyden örnek vererek kafalarında canlanmasi icin öyle deninmiştir.

Aslında coroutineler suspend edilebilen yani efendim siz burada biraz durun biz birazdan gelicez türünde durdurabildiğiniz routinelerdir. Ve bu routineler birbirleri arasında çok kolay geciş yapabilirler. O zaman neden lightweight thread demisler efendim dediginizi duyar gibiyim.

Simdi thread dedigimiz sey nedir diye dusunun sonra bu threadi ne icin kullandiginizi dusunun sonrada bundan milyon tane yarattiginizi dusunun ne olur. Out-of-memory hatasi olur ne olacak. İşte thread ile yaptiginiz islemleri coroutine ile yapip milyon tane coroutine yaratiginizda ne olur. Cevap tabiki ne islemi yapiyorsaniz o olur efendim. O yuzden adamlar demislerki bu lightweight bir thread olarak dusunebilirsiniz.

Peki bunu sağlayan nedir derseniz. Threadler ağır abilerdir. O yüzden onları oluşturmanın maliyeti çok fazladır. Kendi stackleri olur varsa kendi hafızaları olur. O yüzdende threadler hafıza kullanımına doğrudan bağımlılardır. Ne kadar çok thread o kadar hafıza kullanımı. Bu ağır abileri eşzamanlı — concurrently calıştırmak derttir. Öteki abiye-threade gecmek derttir.

Ama coroutine öylemi evin küçük kardeşi gibidir. Devamlı gönder oraya buraya, hop bi orada hop bi burada. Evin küçük kardeşleri arasında geçiş yapmakta kolay. Ötekini bakkalamı gönderdin tamam ozaman diğerini de manava gönder sıkıyorsa gıkı ciksin. Sonra bekle evde ayakalarını uzatıp onları, getirsinler.

O zaman threadlere ne gerek var abi. Her yer coroutine olsun abi diyenler varsa orada durunuz efendim (Bence bastan okuyunuz). Olay thread vs coroutine değil. Threadler sineği öldürmek için kullanıdığımız balyoz gibi sinek gidiyor ama yanında da götürdükleri var ama yarın bir gün duvar yıkmak istersen sinek öldürücü değil balyoz istersin. Tahmin ediyorum ki bu yazıyı okuduğuna göre yazılım ile ilgilisin o zaman temel şart şudur, bilirsinki neyin neyden iyi olduğu değil, neyi nerede kullanırsan iyi olcağını bilmek önemli. O yüzden yaşasın coroutineler ölsün threadler gibi bir durum söz konusu değil. He birde coroutine mantığı çok eskiden beri olan mantık. Dersenki neden şimdi popi oldu. Dünya micro servislere merak sardı da ondan. Dersen ki ne alakası var, başka zamana.

Bide bu arada coroutine dediğimiz şey callback aslında ama çaktırma yola devam. bkz: Continuation<T>

Tatava Yapma Kod Göster

Dependencyimizi module gradle ımıza ekliyoruz. (Türkçe mahvoldu ama adamlar bulmuş. Koymuş adını ne yapalım.)

Nasil yani coroutine lib mi diyorsunuz degil mi? Aslinda bir nevi evet dilin içersinde sadece suspend keyworddu bulunuyor. Geri kalan özellikler lib ile geliyor.

Olay aslında şu suspend edilebilen ve suspend edilemeyen diye iki dünya oluşturmuşlar ve bu iki dünyayı lib ile sağlanan özellikler ile beraber kullanabilir hale getirmişler. (Yine tatava başladı.)

suspend fonksiyonlari yani bi iş hallettirip o işi hallederken bekleyeceğiniz fonsiyonları. Şöyle yazıyoruz.

Buradaki suspend keyword du bu fonksiyonun suspend edilebilir olduğunu yani işi bitene kadar durucağını ama aynı zamanda var olduğu threadin (eğer biz isteyerek blocklamazsak) blocklanmayacağını belirtiyor. Bir keyword nelere kadir.

Delay i coroutine nin Thread.Sleep() i olarak düşünebilirsiniz. Threadi bloklamaz ama coroutine nin verilen süre kadar beklemesini sağlar. Evet Thread ve coroutine farklı coroutine nin beklemede olması demek thread inde bekliyor olduğu manasına gelmiyor. Kod akıyor gidiyor valla durduramıyoruz (istemezsek tabi). Aklında hep olsun coroutine ayrı thread ayrı dünyalar. Coroutine single thread üzerinde çalışıyor bile denebilir.

Şimdi buradaki kritik nokta şu bu fonksiyonu nasil kullanacağız. Cevap Coroutine builder ile

runBlocking, launch, async ve withContext( Bu builder değil coroutine context. Ama çaktırmayın yazı bütünlüğü için lazım)

runBlocking()

Adı üstünde çalıştığı thread içersindeki coroutine sonlana kadar blokluyor. Test yazarken veya regular code içersinde suspending fonksiyon çalıştırmak istediğiniz zaman kullanılması tavsiye ediliyor. örn: main fonksiyonu.

Diğer builderların aksine suspend fonksiyon değildir, zaten regular fonsiyonların içerisinden direk olarak çağrılabilir olmasının sebebi de budur.

launch()

Çalıştığı Threadi bloklamaz. Job objesi döndürür. Ateşle ve unut gibi bir cümle ile açıklıyorlar. Launch ta zaten buradan geliyor.

Döndürdüğü job objesi ile job u iptal edebiliyoruz. CoroutineScope un extension fonsiyonudur. Yani kullanmak için CoroutineScope una ihtiyacınız var.

GlobalScpoe kullanabilirsiniz ki kullanmayın eğer işleminizin cevabı sizin için app lifecyle boyunca önemli değilse( çoğunlukla değildir).

Gidin Android yazıyorsanız activitynizde kendi coroutinescope unuzu yaratıp oradan kullanın bakmayın siz internetteki kolaycılara.

Misal Activity için data çektin kullancı kapattı activityi eee sende globalscope ta launch etmişin ne olacak? Gelsin memory leakler gitsin crashler. Üzülürsün yapma!

Temel mantik bu. Size uygun dispatcher ile kodunuzu bu şekilde yazabilirsiniz. Job objesi sizin artık parent job objeniz oldu bu sayesede OnStop() da cancel edebilir ve böylece child jobların hepsini durdurabilirsiniz. bkz Structured concurrency

Async()

Launchin aksine job dönmez onun yerine future objesi olan Deffered<ResultObject> döner.

Deffered ta jobtan türemiştir. O sebeple deffered ı iptal ederek coroutine ni iptal edebilirsin.

Defferedtan result almak için await() fonsiyonunu kullanmak gerekiyor. Eğer async içersinde hata oluşursa await() çağrıldığı zaman throw ediliyor.

Bunlar benim ne işime yarayacak diyorsanız. Şimdi hazır olun kotlin coroutineleri default olarak sequantial calışır yani sırayla. Siz coroutinize paralellik katmak isterseniz async kullanmalısınız. Launchtan farkı bu.

Yine aynı şekilde coroutinescope extension fonksiyonu olduğu için çalıştırmak için coroutinescope una ihtiyacınız var.

Böylece 20 saniye sürmesi gereken işlem 10 saniye sürüyor. Yok efendim ben illa 20 saniye sürdürücem derseniz async fonksiyonu Coroutine.Lazy olarak start edebilirsiniz. Böylece await() cağrılana kadar async işlem yapmaz.

withContext()

Efendim şimdiye kadar coroutinescope diyip durduk şimdi de biraz da coroutine context demeyelim mi?

Coroutinecontext aslında sizin coroutine inze ait bilgileri tutan bir yapı.

Context içeriğinde Job ve CoroutineDispacther bulunuyor.

Haydaaa dediğiniz duyar gibiyim, hani context ten bahsedecektik. Ama efendim ben ne yapayim adamlar yazmışta yazmış.

CoroutineDispatcher hangi thread te calışacak bu coroutine main mı io mu onu tayin ediyor. İşte withContextte main, io vb. arasındaki olan geçişleri sağlıyor.

Hatırlayın hop orada hop burada demiştik işte onu sağlayan bu fonksiyon.

Görüyorsunuz bir orada coroutineler bir burada. Bunu neden kullanayım dersen genelde io başlatılan işlemin sonucunu withContext ile ui(main) da göstermek için kullanılıyor.

Toplu bir terim Hatırlatma seansı

CoroutineScope

Ne dedik suspend fonksiyonlar ya başka suspend fonkiyonların içersinde çağrılır yada coroutinescope içersinden çağrılır. Yada demekdik. O yüzden coroutinescope yaratmak şart.

CoroutineContext

Coroutine calışırken bazı bilgiler gerekli olacak, sonuçta buda bir kod elbelt ihtiyacları olacak, onu da buradan veriyoruz. Job ve Dispatch(daha önceden demiştim ama tekrar önemli bilgiyi hafızada tutar.)

CoroutineDispatchers

Bu coroutine hangi ortamda nerelerde calışacak.

Dispatchers.Default
CPU kullanımın önemli olduğu işlerde kullanılır.

Dispatchers.IO
Adı üstünde IO işlemlerinde kullanılır. Efendim sunucuya istek atıcam işte o bu.

Dispatchers.Unconfined
Bu tuhaf biraz kullanmayın diyorlar. Kısaca suspend fonksiyon cağrılana kadar var olan thread calışıyor ama sonra suspend döndükten sonra dönen suspend in thread geçiyor. Kafalar yandı ise devam edelim. İşte o yüzden kodta normalde kullanınmaması gerekir diyor.

Dispatchers.Main
Android lib eklenince geliyor. Android ui thread için kullanılıyor

newSingleThreadContext()
İlla ben kendi thread mı yaratıp kullanıcam derseniz, onun içinde bu fonksiyonu kullanabilirsiniz. Thread yarattıktan sonra işiniz bitince sonlandırmayı unutmayın. Üzülürsünüz.

Job

Bu coroutine nasıl ulaşıcaz. İptal etme vs

Deffered

future, promise vb bana göre havalı callbacklerin kotlincesi

Suspend

Fonskiyonun suspend edilebilir olduğunu belirtmemizi sağlar.

RxJava vs Coroutine

Şimdi akıllara doğal olarak bu karşılaştırma gelmiş olabilir. Ama bu karşılaştırma elma ile armut karşılaştırmaya benziyor. O yüzden yapma etme gel dön bu yoldan bu tür yazıları da tıklayıp prim verme.

Buradan nerelere gidilir

Şimdi efendim bunlar biraz temeli işin bunlar hemen tek yazı ile anlaşılacak şeyler değil. O yüzden bu kavramları iyicene kavradıktan sonra Structured concurrency, hata olursa bu coroutine nasil davraniyor, coroutineler arasinda bilgi alışverişi nasıl olur vb gibi konulara geçebilirsiniz.

Bakılasılar

ve google coroutine kotlin yazınca çıkan ve ciddi ciddi activity işlemi için GlobalScope kullanmayan bir sürü yazı. Hadi örnek bir nebze kabul edilebilir.

Hey sen buraya kadar okuyan koca yürekli insan umarım işine yaramıştır. yazım yanlışları ve diğer her türlü yanlış için kusuralar bakılmasın. Sende anla beni ingilizce klavye + inglizce konular bu kadar Türkçe oluyor. Bir gün belki bir editleme şansım olur.

End of Fun

--

--