← Back to list

Performans Farklarıyla: Async Programlama, Thread Yönetimi ve I/O vs CPU-Bound İşlemler

Asenkron programlamada işlemlerin cpu-bounded ya da i/o-bounded olması ile kullanılan platformun Thread Pool ya da Main-UI Thread üzerinden…

Berkay Coşgun · 2025-06-02 19:37 · 60 claps · 4.4 min read
#async #cpu-bound #sync
Open on Medium ↗

Performans Farklarıyla: Async Programlama, Thread Yönetimi ve I/O vs CPU-Bound İşlemler

Asenkron programlamada işlemlerin cpu-bounded ya da i/o-bounded olması ile kullanılan platformun Thread Pool ya da Main-UI Thread üzerinden çalışması performansı doğrudan etkileyebilir. Bu farkları anlamak için oluşturduğum senaryolar üzerinden birlikte test edelim.

Teste geçmeden önce bilinmesi gereken kavramlar;

Cpu Bounded işlemler: İşlemciyi yoğun kullanan işlemleri temsil eder ve arka planda worker threadler ile çalışır. Thread hiçbir zaman boşta kalmaz ve sürekli hesaplama yapar. .NET ortamında thread pool max-default 32767 tane worker thread tanımlıdır. Cpu bounded thread şeklinde de adlandırılmaktadır.

I/O Bounded işlemler: I/O-bound işlemler, dosya okuma, veri tabanı sorgusu veya ağ üzerinden veri alma gibi işlemlerdir. Asenkron kodlandığında arka planda IOCP mekanizması ile çalışır.

Context Switch: İşletim sisteminin bir işlemden ya da thread’den diğerine geçerken yaptığı geçiş işlemidir. Bu sırada CPU mevcut işlemin durumunu (örneğin register’lar, program sayacı vb. bilgiler) kaydeder ve ardından sıradaki işlemin kaldığı yerden devam edebilmesi için gerekli bilgileri yükler. Bu geçiş her ne kadar kısa sürse de, sık sık yapılması sistem performansını olumsuz etkileyebilir.

İlk senaryo ile teste başlayalım;

Senaryo 1: CPU bounded işlemin Main-UI thread ortamında test edilmesi

 private void Calculate()
 {
     for (long i = 0; i < 800000000; i++)
     {
         Console.WriteLine("Hello");
     }
 }

Yukarıdaki Calculate() fonksiyonu ile arka planda worker threadimizi sürekli meşgul edecek bir cpu-bound işlem oluşturduk.

Calculate() fonksiyonunu aşağıdaki kodda olduğu gibi Task.Run yöntemi ile async çalıştırıp test edelim.

private async void Button_Async(object sender, EventArgs e){
  await Task.Run(() => Calculate());
}

Main-UI Thread - CPU Bounded - Async:

Şimdi de Calculate() fonksiyonunu aşağıdaki gibi senkron şekilde çalıştıralım ve sonuçları inceleyelim.

private void Button_Sync(object sender, EventArgs e){
  Calculate();
}

Main-UI Thread - CPU Bounded - Sync:

Görüldüğü gibi ilk dikkat çeken fark UI donması, cpu-bounded bir işlemi Task.Run ile asenkron çalıştırmak, Main-UI Thread ile çalışan uygulamalarda (Windows form, Mobil applications) performans avantajı sağlar ve arayüzün donmasını engeller. Çünkü UI thread, yanıt beklerken hesaplama işlemiyle meşgul olursa arayüzde donma meydana gelir.

CPU-bound bir işlemi Task.Run ile asenkron çalıştırmak, context switch maliyeti oluşturması nedeniyle dezavantajlı olabilir. Bundan dolayı yoğun olmayan ve kısa sürede tamamlanan işlemlerde senkron programlama tercih edilebilir. Ancak kullanıcı arayüzünün donması, kullanıcı deneyimini olumsuz etkileyebileceğinden ve yoğun hesaplama gerektiren işlemlerde performans kaybına neden olacağından dolayı context switch maliyeti göz ardı edilebilir.

Senaryo 2: Cpu Bounded işlemin Thread Pool ortamında test edilmesi

Aynı kodları Thread Pool ortamında test ettiğimizde nasıl bir performans farkı oluşacak onu inceleyelim.

Thread Pool - CPU Bounded - Sync:

Şimdi de cpu-bounded işlemi Task.Run ile asenkron çalıştırıp inceleyelim.

Thread Pool - CPU Bounded - Async:

Dikkat edilirse thread’lerin hemen hemen aynı performansta istekleri işleyebildiği ve senaryo 1'de olduğu gibi herhangi bir ui donması yaşanmadığı görülmekte. Ancak thread havuzu daha verimli kullanılıyor. Örneğin 4, 5, 7 ve 8 numaralı işlemlere thread: 15 yani 4 adet, thread: 26 3 adet ve thread:27 2 adet istekle ilgileniyor.

Tabii ki context switch maliyetini burada dikkate almak uygulamanın performansı açısından daha sağlıklı olur. Yapılan cpu-bounded işlem çok yoğun hesaplama içermiyorsa thread pool ortamında senkron kodlama yapmak daha sağlıklı olacaktır.

Ayrıca belirtmek gerekir ki, Thread Pool ortamında UI thread doğrudan iş parçacığına bağlı olmadığından dolayı arayüzde herhangi bir donma yaşanmadı.

Senaryo 3: I/O bounded işlemin Main-UI Thread ortamında test edilmesi:

Async programlamanın asıl odak noktası olan I/O bounded işlemler üzerindeki performans farkı gerçekten dikkate değer seviyede. Test için io-bounded işlem olarak dosya okuma yapmakla beraber yapay gecikme ekleyeceğim.

FileReadAsync(string filePath) metodunda dosya okuma haricinde Task.Delay(4000) ile 4 saniye gecikme ekledim.

private async Task FileReadAsync(string filePath){
  await File.ReadAllLinesAsync(filePath);
  await Task.Delay(4000);
}

Not: Yukarıdaki kodda olduğu gibi birden fazla async işlemi mümkünse WhenAll() veya WhenAny() gibi yöntemlerle kullanmak daha performanslı olacaktır.

FileRead metodunda ise gecikme için Thread.Sleep(4000) kullandım, şimdi teste geçebiliriz.

private void FileRead(string filePath){
  File.ReadAllLines(filePath);
  Thread.Sleep(4000);
}

Main-UI Thread - I/O Bounded - Sync:

Yukarıdaki sonuca baktığımızda UI Thread ile çalışan ortamda io-bounded işlemin sync şeklinde kodlanması ile cpu-bounded işlemin sync kodlanması arasındaki performans farkı neredeyse yok denilecek kadar az. Ancak async tarafında işler değişiyor.

Main-UI Thread - I/O Bounded - Async:

Async tarafında io-bounded işlemin cpu-bounded işleme göre daha performanslı olduğu gözüküyor çünkü async programlamanın asıl odak noktası bir threadin input/output’ a bağlı veri bekliyorken diğer işlemler ile ilgilenebilmesini sağlamak. Bu şekilde Main-UI thread meşgul olmayacağından dolayı ui kilitlenmez ve async kodlamadan yüksek verim alınır.

Şimdi de thread pool ortamında aynı karşılaştırmayı yapalım ve test sonuçlarını inceleyelim.

Senaryo 4: I/O Bounded işlemin Thread Pool ortamında test edilmesi:

Thread Pool - I/O Bounded - Sync:

Thread Pool ortamında senkron kodlama yaptığımızda, yapılan işlemin CPU-bound olması ile IO-bound olmasının pek fazla farkı yok gibi duruyor; ancak asenkron yapıda işler değişiyor.

Thread Pool - I/O Bounded - Async:

Async tarafında yine ciddi fark var. Dikkat edilirse thread 20, 21, 22, 23 çoğu isteği işleyebiliyor. Bu şekilde thread havuzunu gayet verimli kullanmış oluyoruz ve async programlamadan tam performans almış oluyoruz.

Peki i/o bounded bir işlem asenkron şeklinde çalıştırıldığında arka planda neler oluyor?

I/O bounded işlemler asenkron başlatıldığında, .NET işletim sistemi ile entegre çalışan I/O Completion Ports (IOCP) mekanizmasını kullanarak işlemi başlatır. Bu esnada işlemci kaynaklarını meşgul eden herhangi bir thread kullanılmaz. I/O işlemi tamamlandığında, işletim sistemi IOCP aracılığıyla .NET’e tamamlanma bildirimi yapar. Tamamlanan işlemi işlemek ve await ifadesinden sonraki işlemleri gerçekleştirmek için thread devreye girer. Bu şekilde thread pool ortamında bulunan threadler verimli kullanılmış olur.

SON


메타데이터
post_id
d1a0167c82e7
slug
performans-farklarıyla-async-programlama-thread-yönetimi-ve-i-o-vs-cpu-bound-i̇şlemler-d1a0167c82e7
url
https://medium.com/@berkaycosgun01/performans-farklar%C4%B1yla-async-programlama-thread-y%C3%B6netimi-ve-i-o-vs-cpu-bound-i%CC%87%C5%9Flemler-d1a0167c82e7
canonical_url
https://medium.com/@berkaycosgun01/performans-farklar%C4%B1yla-async-programlama-thread-y%C3%B6netimi-ve-i-o-vs-cpu-bound-i%CC%87%C5%9Flemler-d1a0167c82e7
author_url
https://medium.com/@berkaycosgun01
status
ok
fetched_at
2026-08-05 09:21:10