C# Process ve Thread Yönetimi
C#’ta process ve thread yönetimini öğrenin. Çok iş parçacıklı yapı, süreç kontrolü ve sistem kaynaklarıyla çalışma örneklerle.
Modern uygulamalarda birden fazla görevi aynı anda yerine getirmek için işlem (process) ve iş parçacığı (thread) kavramları kullanılır. C# ve .NET, bu iki yapıyı yönetmek için güçlü API’ler sunar. Process bir uygulamanın çalışan örneğidir; Thread ise o işlem içinde paralel çalışan alt akışlardır. Bu makalede süreç (process) başlatma, iş parçacığı (thread) yönetimi, senkronizasyon ve paralel çalışmanın temelleri anlatılmaktadır.
Process (İşlem) Nedir?
Her çalışan uygulama, işletim sistemi tarafından bir process olarak temsil edilir.
Bir process’in kendi bellek alanı, kaynakları ve çalışan thread’leri vardır.
.NET’te process yönetimi için System.Diagnostics.Process sınıfı kullanılır.
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
// Yeni bir Not Defteri işlemi başlat
Process.Start("notepad.exe");
// Çalışan tüm işlemleri listele
foreach (var proc in Process.GetProcesses())
{
Console.WriteLine($"{proc.ProcessName} - ID: {proc.Id}");
}
}
}
Process.Start() metodu yeni bir uygulama başlatırken,
GetProcesses() tüm çalışan işlemleri getirir.
Bir Process Üzerinde İşlemler Yapmak
Process nesnesi üzerinden çalışan uygulamaya erişebilir, kapatabilir veya bekletebilirsiniz.
using System;
using System.Diagnostics;
using System.Threading;
class Program
{
static void Main()
{
// Not defteri başlat
var p = Process.Start("notepad.exe");
// 3 saniye bekle
Thread.Sleep(3000);
// Kapat
p.Kill();
Console.WriteLine("Not Defteri kapatıldı.");
}
}
Bu örnekte process 3 saniye sonra sonlandırılır.
Dikkat: Kill() metodu zorla sonlandırma yapar;
işlemde açık dosya veya veri kaybı olabilir.
Thread (İş Parçacığı) Nedir?
Thread, bir process içindeki yürütme birimidir. Varsayılan olarak her uygulama bir ana (main) thread ile başlar. Farklı görevleri paralel yürütmek için ek thread’ler oluşturulabilir.
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread t = new Thread(SelamVer);
t.Start();
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Main thread çalışıyor...");
Thread.Sleep(500);
}
t.Join(); // Thread’in bitmesini bekler
Console.WriteLine("Program sonlandı.");
}
static void SelamVer()
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Selam thread çalışıyor...");
Thread.Sleep(400);
}
}
}
Bu örnekte iki thread aynı anda çalışır.
Join(), thread’in tamamlanmasını bekler.
Thread Priority (Öncelik) ve Durum Yönetimi
Her thread’in bir Priority (öncelik) değeri ve ThreadState (durum) özelliği vardır.
Thread önceliği CPU zamanlamasını etkileyebilir.
Thread t1 = new Thread(() =>
{
Console.WriteLine("Düşük öncelikli iş.");
});
t1.Priority = ThreadPriority.Lowest;
Thread t2 = new Thread(() =>
{
Console.WriteLine("Yüksek öncelikli iş.");
});
t2.Priority = ThreadPriority.Highest;
t1.Start();
t2.Start();
Console.WriteLine($"t1 durumu: {t1.ThreadState}");
Console.WriteLine($"t2 durumu: {t2.ThreadState}");
Not: İşletim sistemi thread önceliğini dikkate alır, ancak bu garanti değildir; zamanlayıcıya bağlıdır.
Thread Güvenliği ve Senkronizasyon
Birden fazla thread aynı kaynağa (örneğin değişken, liste veya dosya) eriştiğinde senkronizasyon gerekir. Aksi halde “race condition” (yarış durumu) oluşabilir.
using System;
using System.Threading;
class Program
{
static int sayac = 0;
static object kilit = new object();
static void Main()
{
Thread t1 = new Thread(Arttir);
Thread t2 = new Thread(Arttir);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Sonuç: {sayac}");
}
static void Arttir()
{
for (int i = 0; i < 1000; i++)
{
lock (kilit)
{
sayac++;
}
}
}
}
lock anahtar kelimesi aynı anda yalnızca bir thread’in kilitli bloğa girmesini sağlar.
Bu sayede veri tutarsızlığı önlenir.
ThreadPool ile Hafif Thread Yönetimi
ThreadPool, kısa ömürlü işler için thread oluşturma maliyetini azaltır. .NET, mevcut thread havuzundan boşta olan thread’leri otomatik kullanır.
using System;
using System.Threading;
class Program
{
static void Main()
{
for (int i = 0; i < 5; i++)
{
int id = i;
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine($"İş {id} başlatıldı (Thread ID: {Thread.CurrentThread.ManagedThreadId})");
Thread.Sleep(500);
});
}
Console.WriteLine("Tüm işler gönderildi.");
Thread.Sleep(2000); // ThreadPool’un bitmesini bekle
}
}
ThreadPool genellikle kısa süren ve sık tekrarlanan görevlerde tercih edilir (örneğin HTTP istekleri, log işlemleri).
Process ve Thread Arasındaki Fark
| Özellik | Process | Thread |
|---|---|---|
| Tanım | Çalışan programın örneği | Process içinde paralel çalışan yürütme birimi |
| Kaynak Kullanımı | Kendi belleği vardır | Aynı bellek alanını paylaşır |
| Bağımsızlık | Bağımsız çalışır | Process’e bağlıdır |
| Oluşturma Maliyeti | Yüksek | Düşük |
| İletişim | IPC (Inter-Process Communication) gerekir | Paylaşılan bellek ile kolay iletişim |
Örnek: Paralel Dosya İndirme
Aşağıdaki örnekte her dosya indirme işlemi ayrı thread’lerde çalıştırılır. Böylece birden fazla dosya aynı anda indirilebilir.
using System;
using System.Net;
using System.Threading;
class Program
{
static void Main()
{
string[] dosyalar = {
"https://example.com/dosya1.jpg",
"https://example.com/dosya2.jpg",
"https://example.com/dosya3.jpg"
};
foreach (var url in dosyalar)
{
new Thread(() => DosyaIndir(url)).Start();
}
Console.WriteLine("İndirmeler başlatıldı...");
}
static void DosyaIndir(string url)
{
string ad = Path.GetFileName(url);
using var client = new WebClient();
client.DownloadFile(url, ad);
Console.WriteLine($"{ad} indirildi. (Thread: {Thread.CurrentThread.ManagedThreadId})");
}
}
Bu örnek, IO tabanlı işlemlerde paralel çalışmanın büyük hız kazandırabileceğini gösterir.
Performans ve Dikkat Edilecekler
- Çok fazla thread oluşturmak sistem kaynaklarını tüketebilir.
lockbloklarını uzun tutmak performansı düşürür; mümkünse kısa işlemlerle sınırlayın.- CPU tabanlı işler için
Task Parallel Library (TPL)tercih edin. - IO tabanlı işlemler için
async/awaitasenkron yapısı daha verimlidir. - Process’ler arasında veri paylaşımı yoktur; gerekirse named pipe veya file/socket kullanılır.
TL;DR
- Process, çalışan bir programın örneğidir; kendi bellek alanına sahiptir.
- Thread, process içinde paralel çalışan görev akışıdır; belleği paylaşır.
- ThreadPool kısa ve çok sayıda görev için uygundur.
- lock senkronizasyon için kullanılır, veri tutarlılığını sağlar.
- Fazla thread açmak kaynak israfına yol açabilir;
Taskveyaasynctercih edin.
İlişkili Makaleler
C# Asenkron Programlama Temelleri (async/await)
C#’ta async ve await kullanımını öğrenin. Asenkron işlemler, Task yapısı ve performanslı uygulama akışları örneklerle anlatılıyor.
C# Asenkron Streamler (IAsyncEnumerable)
C#’ta IAsyncEnumerable ile asenkron stream kullanımını öğrenin. Veri akışını adım adım işleme senaryoları örneklerle anlatılıyor.
C# Task Parallel Library (TPL) ve Paralel Programlama
C#’ta Task Parallel Library ve paralel programlamayı öğrenin. Task, Parallel ve eşzamanlı işlem senaryoları örneklerle anlatılıyor.