C# Thread Kavramı – Asenkron Programlama

C# programlama dilinde “Thread” kavramı, çoklu iş parçacığı (multithreading) programlaması için kullanılan temel bir kavramdır. Bir iş parçacığı (thread), bir işlem (process) içinde bağımsız olarak çalışabilen ve kendi yürütme hattına (execution path) sahip olan bir parçadır. Çoklu iş parçacığı programlaması, aynı anda birden fazla işi işlemek ve daha iyi performans elde etmek amacıyla kullanılır.

C# dilinde Thread ile ilgili önemli kavramlar şunlardır:

Thread Oluşturma: Thread oluşturmak için System.Threading ad alanındaki Thread sınıfını kullanabilirsiniz. Yeni bir iş parçacığı oluşturmak için bir yöntem veya işlevi iş parçacığı olarak başlatmak için bu sınıfı kullanabilirsiniz.

Thread İş Parçacığı: Thread sınıfı, iş parçacığının kodunu bir delegede veya bir metotda tanımlamanızı sağlar.

Thread İletişimi: Farklı iş parçacıkları arasında veri paylaşımı ve iletişimi, senkronizasyon işlemi kullanılarak yapılmalıdır. Bu, verilerin güvenli ve tutarlı bir şekilde paylaşılmasını sağlar.

Thread Senkronizasyonu: Çoklu iş parçacığı programlamasında, birden fazla iş parçacığı arasındaki sıralama ve erişim çatışmalarını yönetmek için senkronizasyon teknikleri kullanılır. Bu, lock, Monitor, Semaphore, Mutex gibi araçlarla yapılabilir.

Thread Durumu ve Kontrolü: Thread sınıfı, iş parçacıklarını başlatma, duraklatma, sonlandırma ve durumlarını izleme yeteneği sağlar.

Task ve async/await: C# 5.0 ve sonraki sürümlerinde, Task tabanlı çoklu iş parçacığı programlaması için daha yüksek seviyeli bir yaklaşım olan Task ve async/await anahtar kelimeleri tanıtıldı.

Örnek:

    class Program
    {
        static int sharedData = 0; // Ortak veri

        static void Main()
        {
            //Thread tanımlama ve iş parçacığı oluşturma.
            Thread thread1 = new Thread(Gorev1);
            Thread thread2 = new Thread(Gorev2);

            //Thread Durumu ve Kontrolü: Başlat
            thread1.Start();
            thread2.Start();
        }

        static void Gorev1()
        {
            Console.WriteLine("Görev-1 Başladı");
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(1000);
            }
            Console.WriteLine("Görev-1 Tamamlandı Yapılan İş 10");
        }

        static void Gorev2()
        {
            Console.WriteLine("Görev-2 Başladı");
            for (int i = 0; i <= 5; i++)
            {
                Thread.Sleep(1000);
            }
            Console.WriteLine("Görev-2 Tamamlandı Yapılan İş 5");
        }
    }

Yukarıdaki örneğimizde Gorev adlı iki adet metodumuz bulunmakta gorev1 metodunun işi tamamlama süresi 10 saniye olarak ayarlanmıştır. gorev2 nin ise 5 saniye. Senkron bir programlama modelinde çalıştıracak olsaydık eğer önce gorev1 metodu çalışır görevini bitirir sonra gorev2 metodu çalışır ve toplam 15 saniyede tüm görevler tamamlanırdı.

Asenkron modelde ise gorev1 iş parçacığı çalışmaya başladığı anda gorev2 iş parçacığı çalışmaya başlar ve ilk olarak 5 saniye süren görev2 biter ardından 10 saniye süren görev1 metodu işlemini tamamlar. Tüm işlemin süresi toplam 10 saniye sürer.

Sonuç:

Görev-2 Başladı
Görev-1 Başladı
Görev-2 Tamamlandı Yapılan İş 5
Görev-1 Tamamlandı Yapılan İş 10

Thread Metotları

Thread metotları, çoklu iş parçacığı (multithreading) programlamasında kullanılan iş parçacıkları (threads) arasındaki koordinasyonu ve yönetimi sağlayan işlevlere denir. .NET ve C# gibi modern programlama dilleri, çoklu iş parçacığı desteğini kolaylaştırmak için çeşitli thread metotları sağlar.

Thread.Start Metodu: Bir iş parçacığı başlatmak için kullanılır. Bir iş parçacığını yürütmek için bu metodu çağırarak iş parçacığı başlatılır.

Thread myThread = new Thread(MyMethod);
myThread.Start();

Thread.Join Metodu: Bir iş parçacığının diğer iş parçacığı tamamlanmadan beklemesini sağlar. Özellikle ana iş parçacığının diğer iş parçacıklarının tamamlanmasını beklemesi gereken durumlarda kullanılır.

myThread.Join(); // myThread iş parçacığının tamamlanmasını bekle

Thread.Sleep Metodu: Bir iş parçacığını belirli bir süre boyunca uyutur. Bu, iş parçacığının belirli bir süre boyunca uyumasını ve ardından çalışmasını sağlar.

Thread.Sleep(1000); // 1000 milisaniye (1 saniye) boyunca uyut

Thread.Abort Metodu: Bir iş parçacığını derhal sonlandırmak için kullanılır. Ancak bu yöntem güvenli değildir ve kullanılması önerilmez.

myThread.Abort(); // myThread iş parçacığını sonlandır

Thread.Reset Metodu: Bir olayı (Event) beklemek için kullanılır. Bir iş parçacığının belirli bir olayın tetiklenmesini beklemesini sağlar.

ManualResetEvent resetEvent = new ManualResetEvent(false);
resetEvent.WaitOne(); // Olayı beklemek için kullanılır

Thread.Yield Metodu: İşlemci kaynağını diğer iş parçacıklarına bırakmak ve iş parçacığının belirli bir süre uyumasını sağlamak için kullanılır.

Thread.Yield(); // İşlemci kaynağını bırak ve diğer iş parçacıklarına şans ver

ThreadPool.QueueUserWorkItem Metodu: Bir iş parçacığının bir işi (metodu) arka planda çalıştırmak için kullanılır. ThreadPool, iş parçacığı havuzunu kullanarak iş parçacıklarını yönetir.

ThreadPool.QueueUserWorkItem(MyMethod);

Task.Run Metodu (Task Paralell Library – TPL): C# 5.0 ve sonraki sürümlerde kullanılan bir API’dir. İş parçacıklarını yönetmeye yardımcı olan bir dizi yöntem sunar.

Task.Run(() => MyMethod());

Lock Ifadesi

lock ifadesi, C# programlama dilinde çoklu iş parçacığı (multithreading) uygulamalarında kullanılan bir senkronizasyon aracıdır. lock, çoklu iş parçacığı tarafından paylaşılan kaynaklara güvenli bir şekilde erişim sağlamak amacıyla kullanılır. lock kullanıldığında, yalnızca bir iş parçacığı aynı anda ilgili kod bloğuna girebilir, bu da veri paylaşımını güvenli hale getirir.

Örneğin aşağıda örneğimizde görev2 metodunu iki farklı iş parçacığı üzerinden çağırdık ve gorev2 metodunun içerisinde lock anahtar kelimesini kullandık. Eğer lock anahtar kelimesini kullanmasaydık aynı anda iki adet gorev2 metodu çalışmaya başlayacaktı ve 5 saniye içerisinde iki çözüm üretecekti. Ama lock kullandığımız için ikinci görev parçacığı birinci görev parçacığının bitmesini bekleyecektir.

    class Program
    {
        static int sharedData = 0; // Ortak veri

        static void Main()
        {
            //Thread tanımlama ve iş parçacığı oluşturma.
            Thread thread1 = new Thread(Gorev1);

            //Aynı görevi iki iş parçacığı ile çağırma
            Thread thread2 = new Thread(Gorev2);
            Thread thread3 = new Thread(Gorev2);

            //Thread Durumu ve Kontrolü: Başlat
            thread1.Start();
            thread2.Start();
            thread3.Start();

        }

        static void Gorev1()
        {
            Console.WriteLine("Görev-1 Başladı");
            for (int i = 0; i <= 10; i++)
            {
                Thread.Sleep(1000);
            }
            Console.WriteLine("Görev-1 Tamamlandı Yapılan İş 10");
        }

        static void Gorev2()
        {
            lock (typeof(Program))
            {
                Console.WriteLine("Görev-2 Başladı");
                for (int i = 0; i <= 5; i++)
                {
                    Thread.Sleep(1000);
                }
                Console.WriteLine("Görev-2 Tamamlandı Yapılan İş 5");
            }
        }
    }

Sonuç:

Görev-1 Başladı
Görev-2 Başladı
Görev-2 Tamamlandı Yapılan İş 5
Görev-2 Başladı
Görev-1 Tamamlandı Yapılan İş 10
Görev-2 Tamamlandı Yapılan İş 5