Thread tabanlı eşzamanlılık ile async I/O (event loop) arasındaki fark nedir? I/O-bound ve CPU-bound işlerde hangisini seçersiniz?
Cevap
Thread modelinde her istek için bir thread bloklanarak bekler; işletim sistemi thread'ler arasında geçiş yapar. Async I/O modelinde az sayıda thread (çoğu zaman tek) bir event loop çalıştırır; I/O başlatılır, beklenmez, hazır olduğunda kernel bildirir (epoll, kqueue, IOCP) ve ilgili callback/coroutine devam ettirilir.
Karşılaştırma
| Thread başına istek | Async / event loop | |
|---|---|---|
| Bellek | Her thread için stack (Linux'ta varsayılan 8 MB sanal, gerçek kullanım daha az) | Coroutine başına birkaç KB |
| Context switch | Kernel seviyesinde, pahalı | Kullanıcı seviyesinde, ucuz |
| 10.000+ eşzamanlı bağlantı | Zorlanır | Rahat ölçeklenir |
| Kod okunabilirliği | Düz, sıralı kod | async/await ile iyi, ama "function coloring" sorunu |
| CPU-yoğun iş | Çok çekirdeği kullanabilir (dile bağlı) | Event loop'u bloklar, tüm istekler bekler |
I/O-bound ve CPU-bound
- I/O-bound (HTTP çağrıları, veritabanı, dosya): zamanın çoğu beklemekle geçer. Async I/O veya hafif thread'ler idealdir.
- CPU-bound (görüntü işleme, şifreleme, büyük hesaplamalar): bekleme yoktur, çekirdek sayısı kadar paralellik gerekir. Async işe yaramaz; process/thread havuzu kullanılır.
Python örneği
import asyncio, time
from concurrent.futures import ProcessPoolExecutor
async def fetch(i):
await asyncio.sleep(1) # I/O'yu taklit eder, loop'u bloklamaz
return i
def heavy(n):
return sum(x * x for x in range(n)) # CPU-bound
async def main():
t = time.perf_counter()
await asyncio.gather(*(fetch(i) for i in range(100)))
print(f"100 I/O işi: {time.perf_counter() - t:.1f}s") # ~1.0s
loop = asyncio.get_running_loop()
with ProcessPoolExecutor() as pool: # CPU işini loop dışına taşı
await asyncio.gather(*(loop.run_in_executor(pool, heavy, 10**6) for _ in range(4)))
if __name__ == "__main__": # ProcessPoolExecutor için gerekli (spawn/forkserver)
asyncio.run(main())CPython'da GIL nedeniyle thread'ler CPU-bound işi paralel çalıştıramaz. Python 3.13'te deneysel olarak gelen free-threaded (GIL'siz) build, 3.14 ile resmi olarak desteklenir hâle geldi ama hâlâ varsayılan değildir; bu yüzden CPU işleri için ProcessPoolExecutor tercih edilir.
Orta yol: hafif thread'ler
Go goroutine'leri ve Java 21 virtual thread'leri, sıralı (bloklayan görünümlü) kod yazmanıza izin verirken arka planda async I/O kullanır. Bu, thread modelinin okunabilirliğini async modelin ölçeklenebilirliğiyle birleştirir.
Sık hata
Async bir handler içinde bloklayan çağrı (time.sleep, senkron DB sürücüsü, ağır JSON parse) yapmak tüm event loop'u durdurur. Node.js'te de aynı sorun geçerlidir.
İlgili İşletim Sistemleri soruları
- Concurrency ile parallelism arasındaki fark nedir?
- Race condition nedir? Bir örnekle nasıl önleneceğini gösterin.
- Context switch nedir? Neden maliyetlidir?
- Virtual memory ve paging nasıl çalışır? Page fault nedir?
- Mutex ile semaphore arasındaki fark nedir?
- Deadlock nedir? Oluşması için gereken dört koşul nelerdir ve nasıl önlenir?
Bu konuda başka bir soru biliyor musun?
Mülakatta karşılaştığın soruları ekle, onaylandıktan sonra yayına alınsın.