📌 Hızlı Özet & Temel Çıkarım:
Python’da Eşzamanlı Programlama, doğru problemi doğru araçla çözme mühendisliğidir. Global Interpreter Lock (GIL) nedeniyle standart iş parçacıkları (threading) CPU-yoğun işlemlerde gerçek paralellik sağlayamaz. Ağ çağrıları, dosya I/O ve veritabanı sorguları gibi bekleme odaklı işlerde Asyncio Event Loop tek çekirdekle binlerce eşzamanlı bağlantıyı yönetirken; görüntü işleme ve matematiksel hesaplamalarda Multiprocessing ile çoklu çekirdek gücünden tam faydalanılır.
1. Concurrency (Eşzamanlılık) vs Parallelism (Paralellik)
Yazılımcıların en sık karıştırdığı iki kavram:
- Concurrency (Eşzamanlılık): Birden fazla işi aynı anda idare etmektir (juggling). Örneğin bir aşçının fırındaki keki beklerken soğan doğraması. Burada tek bir CPU çekirdeği işler arasında hızlıca geçiş yapar (Context Switch).
- Parallelism (Paralellik): Birden fazla işi tam olarak aynı anda fiziksel olarak yürütmektir. Örneğin mutfakta iki ayrı aşçının yan yana çalışması. Çoklu CPU çekirdeği gerektirir.
Python’ın CPython uygulamasında bulunan GIL (Global Interpreter Lock), aynı anda yalnızca tek bir yerel iş parçacığının Python bayt kodunu yürütmesine izin verir. Bu kısıt, eşzamanlılık stratejimizi doğrudan belirler.
2. Üç Yaklaşımın Karşılaştırmalı Kullanım Tablosu
| Yöntem | Mekanizma | İdeal Görev Tipi | GIL Etkisi | Bellek Ayak İzi |
|---|---|---|---|---|
| Asyncio | Tek thread, Kooperatif Event Loop | Binlerce ağ/web isteği, WebSockets | Etkilenmez (Bekleme anında GIL serbest kalır) | Çok düşük (~birkaç KB) |
| Threading | İşletim sistemi thread’leri | Disk I/O, harici C kütüphaneleri | GIL nedeniyle CPU işlerinde hızlanmaz | Orta (~8 MB / thread) |
| Multiprocessing | Ayrı işletim sistemi süreçleri | Görüntü işleme, Makine Öğrenimi, Şifreleme | GIL’i tamamen aşar (Her süreçte ayrı GIL) | Yüksek (Ayrı RAM alanı) |
3. Asyncio ile Yüksek Hızlı Asenkron HTTP Taraması
Aşağıdaki kod parçası, httpx ve asyncio kullanarak yüzlerce web adresini saniyeler içinde minimal kaynakla nasıl denetleyebileceğinizi gösterir:
import asyncio
import httpx
from typing import List, Dict
async def fetch_status(client: httpx.AsyncClient, url: str) -> Dict[str, any]:
try:
response = await client.get(url, timeout=5.0)
return {"url": url, "status": response.status_code, "latency_ms": response.elapsed.total_seconds() * 1000}
except Exception as e:
return {"url": url, "status": "ERROR", "error": str(e)}
async def audit_urls_concurrently(urls: List[str]):
limits = httpx.Limits(max_keepalive_connections=50, max_connections=100)
async with httpx.AsyncClient(limits=limits) as client:
# Tum istekleri ayni anda event loop'a besle
tasks = [fetch_status(client, u) for u in urls]
results = await asyncio.gather(*tasks)
for r in results:
print(f"[{r['status']}] {r['url']}")
if __name__ == "__main__":
target_urls = [f"https://httpbin.org/delay/1" for _ in range(20)]
# Senkron calissa 20 saniye surecek islem asenkron olarak ~1.2 saniyede biter
asyncio.run(audit_urls_concurrently(target_urls))
4. İlgili Konular ve İç Bağlantılar
- Modern C++ ile Yüksek Performanslı Sistem Programlama — Gerçek donanımsal iş parçacığı yönetimi.
- Deniz Yüzeyinde LiDAR ve Stereo Kamera Füzyonu — Python multiprocessing ile görüntü işleme.
- Astro SSG Mimarisi ile Sıfır JavaScript — İstemci yükünü ortadan kaldırma.
5. Sıkça Sorulan Sorular
Python 3.13 ve sonrasındaki “No-GIL” (serbest iş parçacıklı) modu ne anlama gelir?
PEP 703 ile Python çekirdeğindeki GIL isteğe bağlı (free-threaded) hale getirilmiştir. Bu özellik tam kararlı hale geldiğinde Python iş parçacıkları harici C eklentisi olmadan da çoklu çekirdekte gerçek paralellikle çalışabilecektir.
asyncio.sleep() yerine time.sleep() kullanılırsa ne olur?
Asenkron bir fonksiyon içinde time.sleep() çağırmak ölümcül bir hatadır. Bütün event loop’u bloke eder; o anda bekleyen diğer yüzlerce görevin de durmasına neden olur. Asenkron dünyada her zaman non-blocking olan await asyncio.sleep() kullanılmalıdır.
6. ProcessPoolExecutor ve Paylaşımlı Bellek (Shared Memory) ile Sıfır Kopyalama
Python’da multiprocessing kullanırken en büyük performans kaybı, ana süreç ile alt süreçler arasındaki veri serileştirmesinden (pickle) kaynaklanır. Örneğin 100 MB’lık bir görüntü matrisini alt sürece gönderirken pickle işlemi CPU’yu saniyelerce meşgul eder.
Python 3.8+ ile gelen multiprocessing.shared_memory modülü, işletim sisteminin RAM bloğunu doğrudan paylaşarak sıfır kopyalama (zero-copy) veri aktarımı sağlar:
from multiprocessing import shared_memory
import numpy as np
# Ana surecte paylasimli bellek olustur
arr = np.ones((1000, 1000), dtype=np.float64)
shm = shared_memory.SharedMemory(create=True, size=arr.nbytes)
shared_arr = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm.buf)
shared_arr[:] = arr[:] # Veriyi paylasimli alana yaz
# Alt surec yalnizca shm.name string'ini alarak veriye aninda ulasir (0 ms serilestirme)
# Islem bittikten sonra bellek temizligi:
shm.close()
shm.unlink()
Bu yöntem sayesinde görüntü işleme, yapay zeka çıkarımı ve robotik telemetri analizlerinde alt süreçler belleği şişirmeden ultra yüksek hızda paralel çalıştırılır.
7. Asyncio’da Görev İptalleri ve Kaynak Temizliği (Graceful Cleanup)
Asenkron Python programlarında uzun süren bir görevi iptal ettiğinizde (task.cancel()), görev anında asyncio.CancelledError istisnası fırlatır. Eğer bu istisna doğru yakalanmazsa açık kalan veritabanı oturumları ve TCP soketleri bellekte kilitli kalır.
Kusursuz asenkron temizlik deseni:
async def worker_task():
try:
while True:
await asyncio.sleep(1.0)
except asyncio.CancelledError:
# Gorev iptal edildiginde calisacak temizlik blogu
await close_database_connection()
raise # Istisnayi yukari firlatmak event loop icin zorunludur
Bu disiplin sayesinde sunucu kapatılırken veya mikroservis yeniden başlatılırken havuzdaki tüm bağlantılar nazikçe (gracefully) sonlandırılır.
8. Asyncio ve Senkron Kütüphanelerin Birlikte Kullanımı (to_thread)
Geliştirme yaparken her zaman asenkron sürücüler bulamayabilirsiniz. Örneğin eski bir veritabanı kütüphanesi veya karmaşık bir şifreleme algoritması yalnızca senkron (bloklayıcı) çalışıyor olabilir.
Bu senkron fonksiyonu doğrudan asenkron bir endpoint içinde çağırmak tüm sunucunun donmasına yol açar. Python 3.9+ ile gelen asyncio.to_thread bu sorunu çözer:
import asyncio
import time
def blocking_legacy_calculation(x):
time.sleep(2.0) # Bloklayici senkron islem
return x * 42
async def handle_request():
# Bloklayici isi arka plandaki ayri bir thread havuzuna aktar
result = await asyncio.to_thread(blocking_legacy_calculation, 10)
return {"result": result}
Bu yapı, senkron miras kodları event loop’u kilitlemeden güvenle modern asenkron web mimarisine entegre etmenin en temiz yoludur.