Skip to content

Async, Concurrency ve Parallelism Kavramları

Büyük bir bahçede elma toplayan işçileri düşünün, bahçenin orasında işçilerin topladığı elmaları boşaltıldığı biribirinden ayrı toplama alanları ve bu alanların başında ise işçilerin getirdiği sepetlerdeki elmaların kalite kontrolünü yapan, boyuna rengine göre sınıflandıran bir görevli olsun.

ilk senaryomuzda;

1- İşçi doldurduğu sepeti toplama alanına getirir.

2- Kalite kontrol görevlisi elmaları kontrol eder ve sınıflandırarak ait oldukları toplama alanlarına atar.

3- Kontrol bittiğinde işçi sepetini alır ve elma toplamaya geri döner.

Bu senaryoda her bir işçi elma ayrıştırma süreci boyunca toplama alanında beklediği için uzun kuyruklar oluşacak ve kuyruktaki tüm işçiler sıranın kendilerine gelmesini bekleyerek vakit kaybedecek ve iş uzun zaman alacaktır. İşte bu senaryomuzdaki senkron bir süreç gerçekleşmektedir, akış sıralı bir şekilde ilerlemekte ve bir iş bitmeden diğeri başlamamaktadır.

İkinci senaryomuzda ise;

1- İşçi doldurduğu sepeti toplama alanına getirir.

2- Sepetteki elmaları kalite kontrol görevlisinin kontrol etmesi için toplama alanına boşaltır.

3- İşçi elma toplamaya geri döner.

açıkça görüldüğü üzere, bu senaryomuzda herhangi bir işçi sırada beklemeyecek ve topladığı elmaları boşaltıp, elma toplamaya geri dönebilecektir. Kalite kontrol görevlisi ise işçiden bağımsız bir şekilde süreci bloklamadan, işçilerin getirdiği elmaları kontrol edecek ve sınıflandıracaktır. Bu senaryomuzda ise artık sürecimiz asenkron hale geldi. İşin özünde asenkron işlemler concurrency’i amacına ulaşmak için kullanılan yöntemlerden biridir diyebiliriz. En uç dıştaki parçayı anladıktan sonra, concurrency ve parallelism kavramlarınada kısaca bir göz atmakta fayda var.

Concurrency (Eş zamanlılık): Birden fazla farklı işin yapılabilmesi anlamına gelir. Biraz daha teknik bir ifadeyle açıklayacak olursak; Process: herhangi bir programın çalıştığı andaki halini process olarak tanımlayabiliriz. Thread: processler içinde yer alan işçiler olarak tanımlayabiliriz thread sayesinde bir process üzerinde birden fazla işi aynı anda yapabiliriz. Her bir process en az bir thread içermektedir, processler oluştuğunda otomatik olarak bir main thread oluşur ve processin yaşam döngüsü boyunca çalışır. Daha fazla detaya girmeden asıl kavramımız olan concurrency’e dönecek olursak bir process üzerinde birden fazla threadın çalışabilmesi diyebiliriz. Bizim elma bahçesi örneğimizin 2. senaryosu concurrent bir yapıdadır.(İşçiler elma toplarken aynı zamanda kalite kontrol görevlisi toplanan elmaları kontrol etmektedir.) Aynı zamanda concurrency ifadesi için web sunucularını örnek olarak vermek yanlış olmaz web sunucuları aynı anda birden fazla isteğe cevap verebilir, herhangi bir istek bir diğerinin bitmesini beklemez. burda bir parantez açarak çoğunlukla parallelism ile karıştırıldığını belirtelim.

Parallelism (Paralellik): Elma bahçemize bir toplama alanı ve bir kalite kontrol görevlisi daha eklediğimizde paralellik kavramınıda örneklemiş oluyoruz. (İşçilerin bir kısmı birinci toplama alanına elmalarını götürürken, bir kısmı ikinci toplama alanına elmaları götürecek ve bu alanlarda çalışan görevliler kalite kontrolü biribirinden bağımsız bir şekilde gerçekleştirecektir.) Temelde processler farklı iş birimlerinde aynı anda gerçekleşebildiğinde parallelism kavramını ortaya çıkarmış oluyor.

https://www.baeldung.com/cs/concurrency-vs-parallelism

Python tarafında asenkron programlama yapabilmek için asyncio kütüphanesini kullanırız asyncio ise generatorlerin özel bir versiyonu olan coroutinelerden yararlanır ve yüksek seviyeli bir asenkron programlama imkanı sunar, coroutines: biribirlerini bekletebilen, kaldığı/durdurulduğu yerden çalışmaya devam edebilen (context switching) thread safe olmayan yapılardır. Aslında kısa tanımından da anlaşılabildiği üzere asenkron programlama amacına uygun olduğunu söyleyebiliriz. Temel kavramları geçtikten sonra asıl konumuz olan async mevzusuna geri dönelim ve konunun daha iyi anlaşılabilmesi adına küçük bir örnek yapalım.

# sync_talk.py

import time


def talk():
    print('Hello')
    time.sleep(1)
    print('Good Bye!')
    
def main():
    for _ in range(3):
        talk()
        talk()

start = time.perf_counter()
main()
print(f"finished in {time.perf_counter() - start} secs")
❯ python sync_talk.py
Hello
Good Bye!
Hello
Good Bye!
Hello
Good Bye!
Hello
Good Bye!
Hello
Good Bye!
Hello
Good Bye!
finished in 6.030925874999999 secs

Önce kodumuzun senkron çalışan halini inceleyelim, talk() metoduyla “Hello” ve “Good Bye!” kelimelerini aralarında 1 saniye olacak şekilde çalıştırıyoruz ve sonrasında, bir 3 defa dönen bir loop içinde 2 kez fonksiyonumuzu çağırıyoruz. Çıktıyı incelediğimizde 3 x 2 = 6 saniyede tamamlanmış, her talk() metodu çalıştığında sırasıyla “Hello” ve “Good Bye!” yazdırılmış, şimdi birde aynı programın asenkron halini yazalım.

# async_talk.py

import asyncio
import time


async def greeting():
    print('Hello')
    await asyncio.sleep(1)
    print('Good Bye!')
    
async def main():
    for _ in range(3):
        await asyncio.gather(greeting(), greeting())

start = time.perf_counter()
asyncio.run(main())
print(f"finished in {time.perf_counter() - start} secs")
❯ python async_talk.py
Hello
Hello
Good Bye!
Good Bye!
Hello
Hello
Good Bye!
Good Bye!
Hello
Hello
Good Bye!
Good Bye!
finished in 3.0056470419999997 secs

async keywordu fonksiyonun asenkron çalıştığını belirtiyor, await keywordu ise asenkron çalışan bir fonksiyonun işini bitirip sonucu döndürmesini bekliyor. Senkron programımızdan farklı olarak loop içinde fonksiyonumuzu asyncio.gather() metoduyla çağırdık, bu metod kısaca verilen görevi yerine getir ve sonucu al mantığıyla çalışıyor, asıl sihir ise çıktıları incelediğimizde başlıyor. Bu defa programımız 3 x 2 = 6 saniye sürmek yerine 3 saniyede sona eriyor çünkü talk() metodları birbirini beklemeden aynı anda(eş zamanlı) olarak çalıştılar ve sonuç üretebildiler, ayrıca ekrana yazılan kelimelerin sıralaması “Hello”, “Hello”, “Good Bye!”, “Good Bye!” şeklinde gerçekleşti.

Python tarafında genel olarak veri tabanı işlemleri ve I/O işlemler(network, filesystem vb..) gibi konularda asenkron işlemlerden söz edebiliriz, ancak dikkat etmemiz gereken önemli konulardan biri; await keywordunu gereksiz şekilde kullanarak asenkron yazdığımız ve öyle olmasını beklediğimiz süreci günün sonunda senkron hale getirmemektir. Aynı zamanda asenkron bir sürecin senkron bir süreçten her zaman daha iyi veya daha hızlı olduğunu söylemekte hiç doğru olmayacaktır.

Yazıyı burada noktalarken; threading, coroutines, asyncio gibi konuları başka yazılarda inceliyor olacağız.

Published inGenel

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *