Python'da asyncio ile Asenkron Kod Yazmak: Görev, gather ve Zaman Aşımı
Beceri rehberi

Python'da asyncio ile Asenkron Kod Yazmak: Görev, gather ve Zaman Aşımı

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 5 dk okuma

asyncio, bir programın ağ ya da disk gibi beklemelerini boşa harcamadan aynı anda birçok işi yürütmesini sağlar. Sayfada async/await yazımı, görev oluşturma, eşzamanlı çalıştırma, zaman aşımı ve bloklayan kod tuzağı kısa örneklerle anlatılır.

Bir bakışta

  1. asyncio bekleme süresi uzun, hesaplama yükü düşük işler için uygundur. Ağ istekleri, veritabanı sorguları ve dosya erişimi tipik örneklerdir; işlemciyi yoran hesaplama için değildir.
  2. async def eşyordam üretir; çalışması için await edilmesi ya da göreve çevrilmesi gerekir. Fonksiyonu çağırmak tek başına kodu çalıştırmaz, yalnız bir eşyordam nesnesi döndürür.
  3. Eşzamanlılık için görev gerekir: gather ya da TaskGroup ile birden çok eşyordam birlikte yürür. Arka arkaya await etmek işleri yine sıraya dizer.
  4. Zaman aşımı ve iptal baştan düşünülmelidir. Yanıt vermeyen tek bir istek, sınırsız beklerse tüm akışı kilitler.
  5. Bloklayan bir çağrı olay döngüsünün tamamını durdurur. Senkron kütüphane kullanmak zorundaysanız bunu ayrı bir iş parçacığına taşıyın.

asyncio ne zaman işe yarar?

Bir programın zamanının büyük kısmı bekleyerek geçiyorsa, o beklemeyi başka işler için kullanmak mümkündür. Örneğin bir hizmetin çok sayıda dış adrese istek göndermesi gerekiyor. İstekleri sırayla göndermek, her yanıtı beklerken işlemciyi boşta bırakır. asyncio'da bekleyen iş kontrolü olay döngüsüne geri verir, döngü de o sırada hazır olan başka bir işi çalıştırır.

Burada kritik nokta şudur: asyncio tek iş parçacığında çalışır ve işleri gerçekten paralel yürütmez, sırayla ama iç içe yürütür. Bu yüzden kazanç, bekleme süresinin örtüşmesinden gelir. Ağır sayısal hesaplama gibi işlemciyi yoran işlerde asyncio hız getirmez; orada süreç tabanlı paralellik gerekir. Genel kavram için asenkron programlama sözlük maddesine bakılabilir; burada Python'a özgü uygulama anlatılıyor.

async def, await ve asyncio.run ile ilk program

Bir fonksiyonu async def ile tanımladığınızda onu çağırmak kodu çalıştırmaz, bir eşyordam nesnesi döndürür. Eşyordamın çalışması için ya await ile beklenmesi ya da göreve çevrilmesi gerekir. await yalnız async def içinde kullanılır. Programın en üstünde ise asyncio.run ile ana eşyordam başlatılır.

import asyncio

async def sayfa_getir(adres): yanit = await istemci.get(adres) return yanit.status_code

async def main(): kod = await sayfa_getir("https://ornek.com") print(kod)

asyncio.run(main())

Örnekteki istemci, aiohttp ya da httpx gibi asenkron bir HTTP kütüphanesinden gelen nesneyi temsil eder. Sık yapılan ilk hata, await yazmayı unutmaktır: sonuç yerine eşyordam nesnesi elde edersiniz ve Python, hiç beklenmediğini belirten bir uyarı verir.

Eşzamanlı çalıştırma: create_task, gather ve TaskGroup

Birden fazla işi gerçekten örtüştürmek için eşyordamları göreve çevirmek gerekir. Arka arkaya await eden bir döngü işleri yine sıraya dizer. Yaygın yollar şunlardır:

  • asyncio.create_task: Eşyordamı hemen zamanlar ve bir görev döndürür. Görevin referansını tutun; döngü yalnız zayıf referans saklar ve unutulan görev beklenmedik biçimde silinebilir.
  • asyncio.gather: Birçok eşyordamı birlikte çalıştırır, sonuçları girdi sırasıyla döndürür. Varsayılanda ilk hata yukarı çıkar ama diğer işler iptal edilmez. return_exceptions=True verirseniz hatalar da sonuç listesine girer.
  • asyncio.TaskGroup: Bir bağlam yöneticisidir. Bir görev başarısız olursa kalan görevler iptal edilir ve hatalar bir ExceptionGroup olarak yükselir. Python belgeleri bunu gather'a göre daha güçlü güvenlik garantileri veren, daha modern bir yol olarak tanımlar.

Kısa kural: sonuçları toplu ve hataya dayanıklı almak istiyorsanız gather'ı return_exceptions ile, bir işin çökmesi tüm grubu durdurmalıysa TaskGroup'u seçin. TaskGroup Python'un yeni sürümlerinde vardır; eski sürümde gather ile devam edersiniz.

async with asyncio.TaskGroup() as grup: gorevler = [grup.create_task(sayfa_getir(a)) for a in adresler] sonuclar = [g.result() for g in gorevler]

Şekil 1

Hata davranışı, yöntem seçimini belirler

Eşzamanlı çalıştırma yolları

gatherTaskGroup
Bir görev hata verirseHata yukarı çıkar, diğer işler iptal edilmezKalan görevler iptal edilir
Hataların iletilmesiİlk hata ya da return_exceptions ile listedeExceptionGroup olarak topluca
Sonuç sırasıGirdi sırasıylaHer görevin result çağrısıyla
Ne zamanKısmi başarı kabul edilen toplu isteklerBir hatanın tüm grubu geçersiz kıldığı işler
Kaynak: Python belgeleri: asyncio, Coroutines and TasksMindset Institute

Zaman aşımı ve iptal

Dış bir sisteme istek gönderen her akışta bir üst süre belirlemek gerekir. Yanıt hiç gelmezse görev sonsuza dek bekler ve geri kalan işleri de tutar. Başlıca araçlar şunlardır: asyncio.timeout bir bağlam yöneticisidir; süre dolunca içindeki görevi iptal eder ve bunu TimeoutError'a çevirir. Bu hatayı bloğun dışında yakalarsınız. asyncio.wait_for ise tek bir beklemeyi sınırlar, süre dolunca beklenen işi iptal eder ve TimeoutError verir.

try: async with asyncio.timeout(sure): await sayfa_getir(adres) except TimeoutError: print("yanıt gelmedi")

İptal, görevin içinde CancelledError olarak görünür. Bu hata Exception değil BaseException alt sınıfıdır, bu yüzden genel bir except Exception onu yakalamaz; temizlik için try/finally kullanın. CancelledError'ı yakalarsanız yeniden fırlatmak genellikle doğrudur, çünkü iptali bastırmak Python belgelerinde önerilmez.

Kurgusal bir örnek: bir raporlama servisi, her sabah çok sayıda iç hizmetten veri toplasın. Arka arkaya await edildiğinde toplam süre tüm yanıt sürelerinin toplamı olur; gather ya da TaskGroup ile toplandığında ise en yavaş yanıt kadar sürer. Aynı akışa her istek için bir zaman aşımı eklenirse, yanıt vermeyen tek bir hizmet raporun tamamını geciktirmez; o hizmetin verisi eksik işaretlenir ve rapor diğerleriyle üretilir.

Şekil 2

Süre dolunca iptal, iptal de temizlik demektir

  1. Süre dolartimeout ya da wait_for devreye girer.
  2. Görev iptal edilirGörevin içinde CancelledError oluşur.
  3. Temizlik çalışırtry/finally ile bağlantı ve kaynaklar kapatılır; sonuçta TimeoutError görünür.
Kaynak: Python belgeleri: asyncio, Coroutines and TasksMindset Institute

En sık hata: olay döngüsünü bloklamak

asyncio'nun görünmez tuzağı, async def içinde bloklayan bir çağrı yapmaktır. time.sleep, senkron bir HTTP kütüphanesi ya da büyük bir dosyayı eşzamanlı okumak, olay döngüsünü kilitler; bu sırada diğer tüm görevler beklemek zorunda kalır. Kod hatasız çalışır ama beklenen hızlanma gelmez.

Çözüm iki yönlüdür. Mümkünse işin asenkron karşılığını kullanın: bekleme için asyncio.sleep, istek için asenkron istemci. Asenkron karşılığı olmayan kütüphane için asyncio.to_thread, fonksiyonu ayrı bir iş parçacığında çalıştırıp sonucu eşyordam olarak döndürür. Python belgeleri bunun esas olarak giriş-çıkış bağımlı fonksiyonlar için olduğunu belirtir, çünkü GIL işlemci yükünü iş parçacığında hızlandırmaz.

Bir diğer sık hata, görevleri başlatıp sonuçlarını hiç beklememektir. Başlatılan her görev ya await edilmeli ya bir TaskGroup içinde olmalı ya da referansı tutulmalıdır; aksi halde hatalar sessizce kaybolur.

Şekil 3

Aynı iş, bloklayan ve bloklamayan yazım

Bloklayan
Bloklamayan
Bekleme için time.sleep
Bekleme için asyncio.sleep
Senkron HTTP kütüphanesi
Asenkron istemci ya da asyncio.to_thread
Görev başlatıp sonucunu beklememek
TaskGroup ya da gather ile sonuçları toplamak
Kaynak: Python belgeleri: asyncio, Coroutines and TasksMindset Institute

Öğrenme yolu ve Mindset programlarındaki yeri

Sağlam bir sıra şöyledir: önce senkron Python ve hata yönetimini rahat kullanın; ardından tek bir async def ile asyncio.run, sonra gather ile birkaç istek, sonra zaman aşımı ve iptal, en sonda TaskGroup ve asenkron bir web çerçevesi. Her adımda küçük bir uygulama yazın: birkaç adrese istek atıp sonuçları toplayan bir betik, ardından aynısını zaman aşımıyla.

Mindset kataloğunda Python ile web geliştirme (FastAPI) programı asenkron programlama ve arka plan görevleri modülünü içerir; asyncio'ya odaklı program ise eşzamanlılık modellerinden zaman aşımı, iptal ve geri basınca kadar uzanır. Python'da eşzamanlılık ve performans programı asyncio'yu GIL, iş parçacığı ve süreç yaklaşımlarıyla birlikte karşılaştırmalı ele alır; bu da hangi işin hangi yöntemle çözüleceğine karar vermek isteyenler için uygundur.

İlgili Mindset programı

Asenkron Python (asyncio) ile Yüksek Eşzamanlılık

Sık sorulanlar

Sık sorulan sorular

asyncio ile iş parçacığı kullanmak arasındaki fark nedir?

asyncio tek iş parçacığında, işlerin kontrolü açıkça await noktalarında devretmesiyle çalışır. İş parçacıklarında ise işletim sistemi geçişleri belirler. Bu yüzden asyncio'da yarış durumları daha az görülür ama bloklayan tek bir çağrı her şeyi durdurabilir. Sayısal hesaplama için ikisi de yetmez; orada çok süreçli yaklaşım gerekir.

asyncio kodu her zaman daha hızlı mıdır?

Hayır. Yalnız bekleme ağırlıklı işlerde, örneğin çok sayıda ağ isteğinde kazanç sağlar. Kısa ve tek bir istek için async yazmak hızlanma getirmez, kodu yalnız karmaşıklaştırır. Karar vermeden önce mevcut sürenin nerede geçtiğini ölçün.

gather mı TaskGroup mu kullanmalıyım?

Bir görev başarısız olduğunda diğerlerinin sonucu hâlâ işe yarıyorsa gather'ı return_exceptions ile kullanın. Bir hata tüm grubu geçersiz kılıyorsa TaskGroup, kalan görevleri otomatik iptal ettiği için daha güvenlidir. TaskGroup'un yeni Python sürümlerinde bulunduğunu unutmayın.

Mevcut senkron kütüphaneyi asyncio içinde nasıl kullanırım?

Fonksiyonu asyncio.to_thread ile ayrı bir iş parçacığına verin; sonucu await edersiniz ve olay döngüsü bloklanmaz. Bu yöntem giriş-çıkış ağırlıklı fonksiyonlar için uygundur. Uzun süren hesaplama içeren fonksiyonlarda ise ayrı bir süreç havuzu daha doğru bir seçimdir.

asyncio kodunu nasıl test ederim?

Test çerçevenizin asenkron test desteğini kullanın ve dış çağrıları gerçek ağ yerine sahte bir istemciyle değiştirin. Zaman aşımı davranışını doğrulamak için bilerek yavaş yanıt veren bir sahte nesne yazmak, iptal yolunu da sınamanızı sağlar.

Python asyncio Nasıl Kullanılır? async/await Rehberi