Thread nedir?
Thread (kanal), bir programda çalışacak komutlar dizisidir. UNIX işlemleri, main() fonksiyonuyla başlayan tek bir thread'den oluşur. Thread konseptinden önce birden çok komutu paralel olarak çalıştırmanın yolu, fork() ve exec() sistem fonksiyonlarını kullanmaktı. Ama thread mekanizmasını kullanarak, bir program içinde paralel olarak birden çok işi yapmak mümkündür. Ne var ki bunu yapmak, aşağıda değineceğimiz bazı özel durumlar nedeniyle dikkat isteyen bir iştir.
Neden thread kullanmalıyım sorusuna cevap vermek önemlidir. Bu mekanizma kullanılarak yazılan bir program, onsuz yazılan bir programdan hem daha zordur, hem de daha uzun zaman alır. Dolayısıyla eğer gerçekten kullanmanız gerekmiyorsa, thread kullanmaya çalışmak pek de verimli ve tavsiye edilecek bir durum değildir. Çünkü bu meka
Peki avantajları neler? Üç temel avantajdan bahsedebiliriz: hız, girdi-çıktı verimi ve taşınabilirlik. Bunları tek tek incelersek:
Hız: Threadler arasında iletişim oldukça hızlıdır. Eğer algoritmanız hızlı iletişim gerektiriyorsa thread mekanizmasını kullanmak için iyi bir sebebiniz var demektir.
Girdi-Çıktı(I/O) verimi: Normal bir UNIX işleminde, bloklanmış bir girdi-çıktı isteği bütün işlemin durmasına ya da beklemesine sebep olabilir. Ama thread mekanizmasında bir bloklama isteği sadece isteği yapan thread'i ilgilendirdiği için programın geri kalanı bundan etkilenmez.
Taşınabilirlik: POSIX thread'ler, çok az bir hız kaybıyla farklı platformlara taşınabilir. Burada taşınabilirlikten kasıt farklı işletim sistemlerinden çok farklı mimarilerdir.
Thread tanımlaması
Nasıl her işlemin(process) bir işlem tanımlayıcısı (ID) varsa, thread'lerin de IDsi vardır. Aralarındaki fark şu ki process kimlikleri sistem bazında verilirken, thread kimlikleri ait oldukları process bazında verilir.
Thread kimliklerinin veri türü 'pthread_t' dir. Bu veri türüne integer türü gibi davranmak ve bu mantıkla üzerinde işlem yapmak mümkün değildir. Ama thread kimliklerini karşılaştırmak için de fonksiyon vardır.
#include <pthread.h> //eşitse sıfır, değilse sıfır harici değer döner
int pthread_equal(pthread_t tid1,pthread_t tid2);
pthread_t, bir yapıdır(struct). Yapı olması nedeniyle de değerini yazdırmanın taşınabilir bir yöntemi yoktur.
Bir thread'in ID'sini pthread_self fonksiyonu ile elde eder.
#include <pthread.h>
pthread_t pthread_self(void);
Thread yaratmak
Unix process modelinde, her process sadece bir thread kontrol eder. Thread tabanlı modelde de aynı durum geçerlidir. Her process sadece bir thread'den oluşur. Yeni thread'ler program çalışırken oluşur.
Yeni bir thread, pthread_create fonksiyonuyla yaratılır.
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
void *(*start_rtn) (void), (void) *restrict arg);
tidp ile gösterilen hafıza adresi, yeni yaratılan thread'in ID'sini tutar. Yaratılan thread'in başlangıç adresi start_rtn'dir.
Bir thread yaratıldığında hangi thread'in önce çalışacağı konusunda bir garanti yoktur. yaratılan thread de olabilir çağıran thread de. Yeni yaratılan thread, yaratan thread'in adres alanına erişebilir. Ama bekleyen sinyal listesi bu thread için temizlenir.
pthread fonksiyonları, hata olduğunda “errno” kullanmak yerine bir hata kodu dönerler.
thread yarattığınız zaman, main fonksiyonununzda bir sleep kullanmanız tavsiye edilir. Çünkü ana işleminiz bir thread bitmeden önce bitebilir, bu da hataya sebep olur.
Thread yok etmek
Bir işlem içinde herhangi bir thread exit, _Exit ya da _exit fonksiyonlarından birini çağırırsa, bütün işlem sonlandırılır.
Bütün işlemi sonlandırmadan bir thread'i sonlandırmanın ise üç yolu vardır:
thread, start rutininden döner. Dönüş değeri thread'in exit kodudur.
Bir thread, aynı işlemdeki bir thread tarafından iptal edilir.
thread, pthread_exit fonksiyonunu çağırır.
#include <pthread.h>
void pthread_exit(void * rval_ptr);
Buradaki rval_ptr, türü olmayan bir göstericidir. Bu gösterici, işlemlerdeki diğer thread'ler tarafından pthread_join fonksiyonu sayesinde erişilebilir.
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
Bu fonksiyonun detaylarını incelemenizi tavsiye ederim.
Bir thread, aynı işlemdeki başka bir thread'i pthread_cancel fonksiyonu ile yokedebilir.
#include <pthread.h>
int pthread_cancel(pthread_t tid);
Thread senkronizasyonu
Multi-thread programlar yazarken en kritik nokta, paylaşılan bilgilerin kontrolüdür. Eğer bir thread diğer thread'lerin üzerinde değişiklik yapamayacağı bir veriyi okuyorsa sorun yoktur. Ama bir threadin okuduğu veriyi başka bir thread'in değiştirebilme yeteneği varsa bu noktada dikkat edilmesi gerekir. Çünkü birinin okuduğu değeri diğeri değiştirdiğinde tutarsızlık olur ve bu da programınızın düzgün çalışmasını (belkide çalışmasını) engeller.
Bu sorunu aşmanın çeşitli yolları vardır. Biri bilgisayar mimarisini buna göre düzenlemektir ki bir yazılımcı bunun varolduğuna güvenemez. İkinci yol değiştirilen değerlere kilit koymaktır(lock). Ayrıca thread senkronizasyonu da bir yöntemdir. Senkronizasyon en kısa tabiriyle thread'lerin bir değişiklik yapacakları zaman diğer thread'lerle birlikte hareket etmesi ve birinin yaptığı değişiklikten diğerlerinin de haberdar edilmesidir.
Kısa bir İnternet araştırması sonucu bu konuyla ilgili birçok örnek kod bulabilirsiniz.
Mutex
pthread mutex(mutual-exclusion) kullanarak verilerin aynı anda sadece bir thread tarafından değiştirilebileceğini garanti ederek verileri koruyabiliriz. Mutex, bir veriyi değiştirmeden hemen önce diğer thread'lerin veriye erişimini engellemek üzere konulan bir kilit olarak ifade edilebilir.
Mutex mekanizması sadece thread'lerin aynı veri erişimi kurallarını uyguladığı durumlarda işe yarar.
Mutex değişkenleri, pthread_mutex veri türüyle ifade edilir. Mutex kullanmadan önce ya PTHREAD_MUTEX_INITIALIZER sabiti ya da pthread_mutex_init fonksiyonu ile başlatılmalıdır. Eğer mutex, malloc kullanarak dinamik olarak yerleştirildiyse, hafızayı boşaltmadan önce pthread_mutex_destroy fonksiyonu çağrılmalıdır.
#include <pthread.h>
int pthread_mutex_init (pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy (pthread_mutex_t *mutex);
Yukarıda bahsettiğim kilitleme işlemlerini gerçekleştirmek için de aşağıdaki fonksiyonlar kullanılır.
#include <pthread.h>
int pthread_mutex_lock (pthread_mutex_t *mutex);
int pthread_mutex_trylock (pthread_mutex_t *mutex);
int pthread_mutex_unlock (pthread_mutex_t *mutex);
Multithread programlama burada bahsettiğimden çok daha uzun bir konudur. Bu sebeple benim yazımı bir başlangıç yazısı ya da kavramlar açısından yol gösterici olarak kabul etmenizi ve konuyu özellikle örnek kodlar inceleyerek daha detaylı ele almanızı tavsiye ederim. Çünkü mutlthread programlama, tehlikeli programlama tekniklerinden biridir. Araştırmalarınız için diğer birçok konuda olduğu gibi bu konuda da güzel bir başlangıç noktasına sahipsiniz: man pthread.h (Tabi eğer Linux kullanıcısı iseniz:D)
Tags: Programlama Linux thread , Comments: 0 ( Add your comment )