Klasik UNIX sistemleri bir thread mekanizmasına sahip değildi. Ancak modern UNIX
sistemlerine thread mekanizması dahil edilmiştir. Thread fonksiyonları POSIX standartlarına
da eklenmiştir. POSIX thread fonksiyonları yalnızca arabirim fonksiyonlardır. Yani thread
mekanizmasının kurulması ve oluşturulması sistemler arasında aşağı seviyeli olarak
farklılıklar gösterebilmektedir. POSIX thread mekanizması Win32 thread mekanizmasına
mantıksal olarak oldukça benzemektedir. UNIX sistemlerinde değişik thread kütüphaneleri de
kullanılabilmektedir. POSIX thread fonksiyonları pthread ismi ile başlar ve bu kütüphaneye
pthread kütüphanesi denilmektedir.
pthread_create Fonksiyonu
Bu fonksiyon thread’i yaratmakta kullanılmaktadır.
int pthread_create(pthread_t *thread,
pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
pthread_t türü sistemden sisteme değişebilmekle beraber tipik olarak unsigned long
biçimindedir. Birinci parametre thread’in ID değerinin yerleştirileceği pthread_t türünden
değişkenin adresini alır (Win32 sistemlerinde hem thread ID’si hem de thread handle değeri
olmak üzere iki kavram vardır. Halbuki POSIX thread fonksiyonlarında yalnızca ID kavramı
vardır). Fonksiyonun ikinci parametresi pthread_attr_t türünden bir adres alır. Bu tür
genellikle bir yapı biçimindedir. Bu parametre thread’e ilişkin çeşitli özellikleri belirlemekte
kullanılır. NULL geçilebilir. NULL geçildiğinde default özellikler kullanılır. Fonksiyonun
üçüncü parametresi geri dönüş değeri (void *), parametresi de (void *) olan bir fonksiyon
adresidir. Yaratılan thread akışı bu adresten başlar. Fonksiyonun son parametresi thread
fonksiyonuna aktarılacak parametredir. Thread fonksiyonlarının hepsinin prototipleri
pthread.h dosyası içerisindedir. Aslında fonksiyonda thread ID’si yerine NULL da
geçilebilir. Tabii bu iyi bir teknik değildir. Fonksiyon başarı durumunda 0, başarısızlık
durumunda sıfır dışı bir değere geri döner. Thread fonksiyonları libc.a kütüphanesinin
içerisinde değildir. Bu fonksiyonlar libpthread.a kütüphanesinin içerisindedir.
/* thread.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 10
void *ThreadFunc(void *parg)
{
for (;;) {
fputc((int) parg, stderr);
}
return 0; // 0 göstericiye atandığında
// NULL anlamına gelir.
}
int main(void)
{
pthread_t tid[SIZE];
int i;
for (i = 0; i < SIZE; ++i) {
if (pthread_create(&tid[i], NULL, ThreadFunc,
(void *) (i + ‘a’))) {
fprintf(stderr, “Cannot create thread!...\n”);
exit(1);
}
}
getchar();
return 0;
}
pthread_exit Fonksiyonu
Bu fonksiyon bir thread akışı içerisinde kendi thread akışını sonlandırmak için kullanılır.
Thread fonksiyonu sonlandığında zaten sistem tarafından bu fonksiyon çağırılmaktadır.
void pthread_exit(void *retval);
Fonksiyonun parametresi thread fonksiyonunun geri dönüş değeridir. Yani thread fonksiyonu
içerisinde return yapmak ile bu fonksiyonun çağırılması aynı anlamdadır.
Thread’lerin Sonlandırılması
Thread’lerin sonlandırılması ile process’lerin sonlandırılması işletim sistemleri için de benzer
mantık içerisindedir. Örneğin process’in sonlandırılması exit ya da _exit fonksiyonu ile
yapılırken, thread’lerin sonlandırılması pthread_exit fonksiyonu ile yapılmaktadır. Şüphesiz
en normal sonlanma biçimi process’in ya da thread’in kendi kendini sonlandırmasıdır.
Thread’lerin de pthread_cancel fonksiyonu ile başka bir thread tarafından zorla
sonlandırılması mümkündür (Win32 sistemlerinde benzer işlem TerminateThread fonksiyonu
ile yapılmaktadır). Bilindiği gibi UNIX sistemlerinde process sonlandıktan sonra handle
alanının boşaltılması iki biçimde gerçekleştirmektedir.
1) Üst process’in wait ya da waitpid fonksiyonu ile beklemesi.
2) Üst process’in sonlanması ile alt process’in üst process’inin init olması ve init tarafından
alt process handle alanının yok edilmesi.
Aynı durum POSIX thread’leri için de söz konusudur. Yani bir thread sonlandığında thread’in
exit kodu thread’in handle alanına yazılır ve exit kod alınana kadar handle alanı korunur.
Thread’in exit kodu pthread_join fonksiyonu ile alınır. Yani pthread_join mantıksal
bakımdan wait fonksiyonu gibi çalışmaktadır. Bu fonksiyon da wait fonksiyonunda olduğu
gibi thread sonlanana kadar bekleme sağlar. Ancak pthread_join fonksiyonunu herhangi bir
thread uygulayabilir (halbuki wait fonksiyonunu ancak üst process uygulayabilmektedir).
int pthread_join(pthread_t thID, void **thread_return);
Fonksiyonun birinci parametresi thread’in ID değeri, ikinci parametresi geri dönüş değerinin
yerleştirileceği göstericinin adresidir. İkinci parametre NULL geçilebilir.
Sınıf çalışması: Bir thread yaratınız, thread içerisinde 1000 kere a harfini ekrana bastırınız,
ana thread’i pthread_join ile bekletiniz.
/* pthread_join.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 10
void *ThreadFunc(void *parg)
{
int i = 0;
static int count = 0;
for (; i < 10; ++i) {
fputc((int) parg, stderr);
}
return (void *) count++;
}
int main(void)
{
pthread_t tid[SIZE];
int i;
void *pReturn;
for (i = 0; i < SIZE; ++i) {
if (pthread_create(&tid[i], NULL, ThreadFunc,
(void *) i + 'A')) {
fprintf(stderr, "Cannot create thread\n");
exit(1);
}
}
for (i = 0; i < SIZE; ++i) {
pthread_join(tid[i], &pReturn);
printf("%d\n", (int) pReturn);
}
return 0;
}
gcc –o pthread_join pthread_join.c -lpthread
Thread Fonksiyonlarının Geri Dönüş Değeri
Thread fonksiyonları POSIX 1.c‘de eklendiği için başarısızlık durumunda errno değişkenini
set etmemektedir. perror fonksiyonu errno’ya bakarak mesaj yazdığından hatanın nedeni
doğrudan bu fonksiyon ile elde edilemez. Ancak hata mesajı strerror fonksiyonu ile
yazdırılabilir. Tüm thread fonksiyonlarının geri dönüş değeri başarı durumunda 0, başarısızlık
durumunda başarısızlığın nedenini anlatan hata kodudur. Yani bu fonksiyonların geri dönüş
değerleri alınıp strerror fonksiyonuna parametre yapılabilir.
Detached Thread’ler
Normal olarak bir thread pthread_join yapılmadıkça handle alanını korur (UNIX
sistemlerinde bir process’in açabileceği maximum thread sayısı belirlenmiştir. Thread’in
handle alanı yok edilmedikçe maalesef thread açık kabul edilmektedir). Thread’ler istenirse
pthread_detach fonksiyonu ile detached moda sokulabilirler. Detached modda olmayan
thread’lere joinable thread’ler denir. Detached thread’ler sonlandığında otomatik olarak
handle alanı yok edilir. Detached thread pthread_join fonksiyonu ile beklenemez. Bu
durumda thread sonlanana kadar bekleme mekanizması başka biçimde kurulmalıdır.
Fonksiyonun prototipi pthread.h dosyası içerisindedir.
int pthread_detach(pthread_t th);
pthread_self Fonksiyonu
O anda çalışmakta olan thread’in ID değerini elde etmekte kullanılır.
pthread_t pthread_self(void);
Ana Thread’in Sonlandırılması ve exit Fonksiyonu
UNIX/Linux sistemlerinde ana thread pthread_exit fonksiyonu ile sonlandırılabilir (aynı
biçimde Win32 sistemlerinde de ana thread Exit_Thread fonksiyonu ile
sonlandırılabilmektedir). UNIX ve Win32 sistemlerinde son thread de yok edildiğinde process
otomatik olarak çekirdek tarafından sonlandırılır. exit ya da _exit fonksiyonları process’i
sonlandırır, process sonlandığında da tüm thread’ler çekirdek tarafından otomatik
sonlandırılmaktadır (main fonksiyonu bittiğinde başlangıç kodu tarafından zaten exit
çağırılmaktadır).
Thread ID’lerinin Karşılaştırılması
POSIX sistemlerinde thread’lerin ID değerlerini temsil eden pthread_t türü herhangi bir tür
olabilir (pek çok sistemde long türündendir). Bu nedenle iki thread’in ID’sini karşılaştıran
ayrı bir fonksiyon yazılmıştır.
int pthread_equal(pthread_t thread1, pthread_t thread2);
Fonksiyon iki ID birbirine eşitse sıfır dışı bir değere, eşit değilse 0 değerine geri döner.
/* pthread_equal.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 10
pthread_t g_mthread;
void *ThreadFunc(void *parg)
{
if (pthread_equal(pthread_self(), g_mthread))
printf("main thread is here!..\n");
else
printf("another thread is here!..\n");
printf("end...\n");
return NULL;
}
int main(void)
{
pthread_t tid;
int result;
g_mthread = pthread_self();
if ((result = pthread_create(&tid, NULL,
ThreadFunc, NULL )) != 0) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
ThreadFunc(NULL);
pthread_join(tid, NULL);
return 0;
}
Dinamik Tahsisat İşlemlerinin Çok Thread’li Sistemlerde Güvenilirliği
Global bir bağlı listenin birden fazla thread tarafından asenkron biçimde kullanıldığını
düşünelim. Özel bir seri hale getirme mekanizması uygulanmazsa thread’ler arası geçiş
sırasında bağlı liste bozulabilir. Dinamik tahsis eden fonksiyonlar da kendi içinde bağlı liste
kullanmaktadır. Peki bu durumda iki farklı thread aynı anda malloc fonksiyonunu kullanırsa
problem oluşur mu? İşte genellikle dinamik tahsisat fonksiyonları kendi içerisinde bir seri
hale getirme mekanizması ile çalışmaktadır. POSIX sistemlerindeki malloc fonksiyonu bu
biçimde thread’ler arası güvenliği olan bir fonksiyondur. Win32 sistemlerinde seri hale
getirme işlemi programcının isteğine bırakılmıştır. C++’ın STL kütüphanesindeki nesne tutan
sınıflar tek thread’li sistemler için tasarlanmıştır. Bu nedenle bu sınıflar kullanılırken seri hale
getirme mekanizması programcı tarafından sağlanmalıdır.
pthread_once Fonksiyonu
Bazen bir kod parçasının yalnızca bir kez, yani yalnızca bir thread tarafından çalıştırılması
istenir. Bu tür durumlarda global değişken ile oluşturulmuş bir bayrak mekanizması ile bunun
karşılanmaya çalışılması probleme yol açar. Şüphesiz bu tür işlemler senkronizasyon
nesneleri ile yapılmalıdır. pthread_once fonksiyonu bunu sağlamaya yöneliktir. pthread_once
fonksiyonu bir fonksiyon göstericisi parametresi alır ve yalnızca bir thread için belirtilen
fonksiyonu çağırır. Bu durumda tüm thread’ler pthread_once fonksiyonunu çağırırlar, ancak
bu fonksiyon içinde belirtilen kod yalnızca bir kere çalıştırılır.
int pthread_once(pthread_once_t *once_control, void (*init_func)(void));
Fonksiyon şüphesiz kendi içerisinde bir flag mekanizması kurmaktadır. Bu flag değişkenini
birinci parametre olarak programcı girmelidir. Bu değişken static ömürlü olmalıdır ve işin
başında PTHREAD_ONCE_INIT ile ilk değer almalıdır. Yani örnek bir kullanım şöyle
olabilir:
pthread_once_t g_init = PTHREAD_ONCE_INIT;
void Func(void)
{
}
pthread_once(&g_init, Func);
fork İşlemi ve Thread’li Çalışma
Çok thread’li uygulamalarda thread’lerden biri fork işlemi yaptığında yeni yaratılan alt
process tek bir threadle çalışmaya başlar. O thread fork işlemini yapan thread’dir.
/* forkthread.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 10
pid_t g_pidChild;
void *ThreadFunc1(void *parg)
{
int i;
g_pidChild = fork();
if (g_pidChild == -1) {
perror("fork");
exit(1);
}
if (g_pidChild == 0) {
g_pidChild = getpid();
for (i = 0; i < SIZE; ++i) {
printf("Child process: %d\n", getpid());
sleep(1);
}
exit(1);
}
wait(NULL);
return NULL;
}
void *ThreadFunc2(void *parg)
{
int i;
for(i = 0;i < SIZE; ++i) {
if (getpid() == g_pidChild)
printf("child process thread 2\n");
else
printf("parent process thread 2\n");
sleep(1);
}
return NULL;
}
int main(void)
{
pthread_t tid1, tid2;
int result;
if ((result = pthread_create(&tid1, NULL,
ThreadFunc1, NULL )) != 0) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
if ((result = pthread_create(&tid2, NULL,
ThreadFunc2, NULL )) != 0) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
Birden çok thread’le çalışırken fork yapıldığında senkronizasyon nesneleri yüzünden
kilitlenme (deadlock) durumu oluşabilir. Bu nedenle fork işleminden sonra böylesi kilitlenme
durumlarını çözüp bazı geri alma işlemlerini sağlamak için pthread_atfork fonksiyonu
düşünülmüştür.
int pthread_atfork(void (*prepare)(void),
void (*parent)(void), void (*child)(void));
Programcı isterse fork işleminden önce bu fonksiyonu çağırarak fork işlemi yapıldığında
sistemden burada belirtilen üç fonksiyonun çağrılmasını isteyebilir. prepare fonksiyonu sistem
tarafından fork yapılmadan önce üst process için çağırılır. parent fonksiyonu fork işleminden
sonra henüz normal akışa geçmeden üst process için çağırılır. child fonksiyonu ise fork
işleminden sonra henüz normal akışa geçmeden önce alt process için çağırılır. Fonksiyon
göstericileri NULL olarak geçilebilir; bu durumda NULL geçilen fonksiyonlar için çağırma
yapılmaz. Fonksiyon başarılıysa 0 değerine, başarısızsa 0 dışı bir değere geri döner.
/* atfork.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 10
void *g_ptr;
void child(void)
{
printf("child after fork!..\n");
free(g_ptr);
}
void *ThreadFunc1(void *parg)
{
return NULL;
}
void *ThreadFunc2(void *parg)
{
g_ptr = malloc(10000);
sleep(10);
return NULL;
}
int main(void)
{
pthread_t tid1, tid2;
int result;
pid_t pidChild;
if ((result = pthread_create(&tid1, NULL,
ThreadFunc1, NULL )) != 0) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
if ((result = pthread_create(&tid2, NULL,
ThreadFunc2, NULL )) != 0) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
result = pthread_atfork(NULL, NULL, child);
if (result) {
fprintf(stderr, "%s\n", strerror(result));
exit(1);
}
pidChild = fork();
if (pidChild == -1) {
perror("fork");
exit(1);
}
if (pidChild == 0) {
printf("Child process created!..\n");
exit(1);
}
wait(NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
Thread Özelliklerinin Değiştirilmesi
Bir thread’in özellikleri denilince onun stack alanının uzunluğu, stack bölgesinin başlangıç
adresi ve joinable olup olmadığı anlaşılır. Thread’lerin diğer özellikleri nispeten daha
önemsizdir. Thread yaratılırken pthread_create fonksiyonunun bir parametresi thread
özelliklerinin belirlenmesinde kullanılan (pthread_attr_t *) türündendir. pthread_attr_t türü
genellikle bir yapıdır. Yani fonksiyon bizden bu yapının adresini istemektedir. Bu yapı
POSIX tarafından standart hale getirilmemiştir. Bu nedenle bu yapıya erişip işlem yapmak
için arabirim fonksiyonlar düşünülmüştür. Bu durumda programcı pthread_attr_t türünden bir
değişken tanımlar. Standart fonksiyonlarla bu değişkenin belirttiği yapının içini doldurur. Bu
yapının adresini de pthread_create fonksiyonuna geçirir.
pthread_attr_t türünden nesneyi önce ilk değerlemek gerekir. Bu işlem pthread_attr_init
fonksiyonuyla yapılır.
int pthread_attr_init(pthread_attr_t *attr);
Thread yaratıldıktan sonra pthread_attr_destroy fonksiyonuyla boşaltılmalıdır.
int pthread_attr_destroy(pthread_attr_t *attr);
Şüphesiz thread özelliklerini barındıran pthread_attr_t türü çeşitli gösterici elemanları
içermektedir. pthread_attr_destroy fonksiyonu bu göstericiler için tahsis edilmiş alanları
boşaltmaktadır. pthread_attr_destroy işlemi thread yaratıldıktan hemen sonra yapılabilir.
Thread özellikleri ile ilgili fonksiyonlar şunlardır:
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
Sistemlerdeki default stack uzunluğu standart olarak belirlenmemiştir. Ancak pthread.h
dosyası içerisinde PTHREAD_STACK_MIN sembolik sabiti ile default stack uzunluğu
belirtilmiştir.
int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
Programcı thread’i yaratmadan önce isterse kendi tahsis ettiği alanı thread’in stack’i olarak
kullanabilir. Eğer adres belirlemesi yapılmazsa stack alanı sistem tarafından tahsis edilir.
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
Bu fonksiyonlar thread’in detach ya da joinable olma durumunu belirler.
/* threadattr.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
void check_error(const char *msg, int result)
{
if (result) {
fprintf(stderr, "%s: %s\n", msg, strerror(result));
exit(EXIT_FAILURE);
}
}
void *thread_func(void *param)
{
int i;
for (i = 0; i < 10; ++i) {
printf("%d\n", i);
sleep(1);
}
return NULL;
}
int main(void)
{
int result;
pthread_t tid;
pthread_attr_t tattr;
result = pthread_attr_init(&tattr);
check_error("pthread_attr_init", result);
result = pthread_attr_setstacksize(&tattr, 100000);
check_error("pthread_attr_setstacksize", result);
C ve Sistem Programcıları Derneği 138
result = pthread_create(&tid, &tattr, thread_func, NULL);
check_error("pthread_create", result);
result = pthread_join(tid, NULL);
check_error("pthread_join", result);
result = pthread_attr_destroy(&tattr);
check_error("pthread_attr_destroy", result);
return 0;
}
Thread’lerin Dışarıdan Sonlandırılması
Bilindiği gibi bir thread’in normal olarak sonlandırılması iki biçimde yapılmaktadır:
1) Thread fonksiyonunun sona ermesi
2) pthread_exit fonksiyonunun çağırılması
Bunların dışında da thread’ler dışarıdan pthread_cancel fonksiyonu ile sonlandırılabilirler. Bu
fonksiyon Win32 sistemlerindeki TerminateThread fonksiyonuna benzemektedir. Ancak
Win32 sistemlerinden farklı olarak POSIX sistemlerinde thread’in sonlandırılma seçenekleri
daha fazladır.
POSIX sistemlerinde thread’ler senkron ya da asenkron biçimde sonlandırılabilmektedir.
Asenkron sonlandırma demek thread’in o anda zorla sonlandırılması demektir (Win32
sistemlerindeki TerminateThread bu biçimde çalışır). Senkron sonlandırmada pthread_cancel
uygulansa bile thread hemen sonlandırılmaz, belirli noktalarda sonlandırılır. Bu durumda
sonlandırılma isteği sisteme iletilir, ancak sistem uygun noktayı bekler. Thread yaratıldığında
default olarak senkron sonlandırma durumu söz konusudur. Thread’in sonlandırma biçimi
pthread_setcanceltype fonksiyonu ile belirlenir.
int pthread_setcanceltype(int type, int *oldtype);
type parametresi senkron sonlanma için PTHREAD_CANCEL_DEFERRED, asenkron
sonlanma için PTHREAD_CANCEL_ASYNCHRONOUS değerini alır. Görüldüğü gibi
fonksiyonda bir thread ID’si yoktur, yani fonksiyon kendi thread’i üzerinde işlemler
yapmaktadır.
Senkron sonlanma hangi noktalarda gerçekleşmektedir? Çeşitli sistem fonksiyonları senkron
sonlanmaya duyarlıdır. Bunların bir listesi vardır. Bu fonksiyonlardan bazıları şunlardır:
open, close, sleep, system, read, write, wait, waitpid
Ayrıca senkron sonlanma noktası pthread_testcancel fonksiyonu çağırılarak da
belirlenebilmektedir. Örneğin, bir döngü içerisinde uygun bir noktada bu fonksiyon
çağırılarak sonlandırma noktası oluşturulabilir. Bu fonksiyon şüphesiz thread için
sonlandırma isteği olup olmamasına bakar ve kendi thread’ini sonlandırır.
Bazen programcı kritik bazı işlemleri yaparken thread’in sonlandırma biçimi ne olursa olsun
bu sonlandırma işlemini devre dışı bırakabilir. Bunun için pthread_setcancelstate
fonksiyonu kullanılır. Fonksiyonun prototipi aşağıdaki gibidir:
int pthread_setcancelstate(int style, int *oldstyle);
Her iki fonksiyon da ikinci parametreye NULL değeri geçilmesi durumunda eski değeri
yerleştirmemektedir. Bu fonksiyonun birinci parametresi PTHREAD_CANCEL_ENABLE ya
da PTHREAD_CANCEL_DISABLE olabilir.
Thrad’in sonlandırılması nihayet pthread_cancel fonksiyonu ile yapılabilir.
int pthread_cancel(pthread_t tid);
Fonksiyonun parametresi sonlandırılacak thread’in ID değeridir.
/* thread_cancel.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
void check_error(const char *msg, int result)
{
if (result) {
fprintf(stderr, "%s: %s\n", msg, strerror(result));
exit(EXIT_FAILURE);
}
}
void *thread_func(void *param)
{
int i = 0, result;
int status;
long d;
result = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &status);
check_error("pthread_setcancelstate", result);
for (;;) {
printf("%d\n", i++);
sleep(1);
for (d = 0; d < 100000000; ++d)
;
pthread_testcancel();
if (i++ > 10) {
result = pthread_setcancelstate(status, NULL);
check_error("pthread_setcancelstate", result);
break;
}
}
return NULL;
}
int main(void)
{
int result;
pthread_t tid;
result = pthread_create(&tid, NULL, thread_func, NULL);
check_error("pthread_create", result);
getchar();
result = pthread_cancel(tid);
check_error("pthread_cancel", result);
result = pthread_join(tid, NULL);
check_error("pthread_join", result);
return 0;
}
Thread’in sonlandırmaya karşı kapatılmış ise sonlandırma durumu ister senkron olsun, ister
asenkron olsun kapatılma durumunda oluşan kapatma isteği saklanır ve açıldığında etki
göstererek thread sonlandırılır.
Thread’ler ve Signal Mekanizması
POSIX sistemlerinde eskiden thread’ler yoktu. Thread mekanizması eklendiğinde zaten var
olan signal mekanizmasının bu yeni sisteme uyumlu olması gerekmişti. Thread’li sistemde
signal mekanizması için gerekli olan birkaç durum vardır:
- Signal fonksiyonu set edilmemişse, bir thread mi, yoksa tüm process mi sonlanacaktır.
- Signal fonksiyonu set edilmişse signal oluştuğunda hangi thread bu fonksiyonu
çağıracaktır.
- Signal’ın mask yapılması thread başına mı gerçekleşecektir, yoksa process temelinde mi
gerçekleşecektir.
Thread’li sistemde de bir signal oluştuğunda çağırılacak fonksiyon belirlenmemişse yalnızca
tek bir thread değil, yine tüm process sonlandırılmaktadır.
Bir signal oluştuğunda birden fazla thread çalışmaktaysa yalnızca bir thread’in mi bu signal’ı
işleyeceği, yoksa hepsinin mi işleyeceği POSIX standartlarında belirlenmemiştir (fakat
işlenmediğinde bundan tüm process’in etkileneceği belirlenmiştir). Bu durum belirsizliğe yol
açtığı için thread başına çalışan çeşitli signal fonksiyonları düşünülmüştür. Thread’siz
versiyonlarda anımsanacağı gibi sigprocmask fonksiyonu signal’i maskelemek için
kullanılıyordu. Yani process’in toplam bir tane signal mask değeri vardı. Thread’li sistemde
her thread’in ayrı bir signal mask değeri vardır. Bu değer pthread_sigmask fonksiyonu ile set
edilir.
int pthread_sigmask(int how, sigset_t *set, sigset_t *oldset);
Görüldüğü gibi bu fonksiyon ile belirli bir signal bazı thread’ler için devre dışı bırakılabilir.
Her thread’in signal mask değeri birbirinden farklı olabilir. Bu durumda bir signal
oluştuğunda signal fonksiyonunu tek bir thread’in çalıştırması isteniyorsa pthread_sigmask
fonskiyonu ile bir thread dışındaki tüm thread’ler bu signal’a kapatılır, yalnızca tek thread bu
signal’a açılır. Bunu yapmanın başka taşınabilir bir yöntemi yoktur.
kill sistem fonksiyonu process’e signal göndermektedir. Yani bu fonksiyon thread’siz sistem
zamanlarında tasarlandığı için yukarıda açıklanan taşınabilirlik problemi oluşmuştur.
POSIX1.c’de bir thread’e signal gönderme kavramı da eklenmiştir. Bir thread’e signal
gönderildiğinde signal fonksiyonu o thread tarafından işletilir. Ancak signal fonksiyonu set
edilmemiş ise yine bundan tüm process etkilenmektedir. POSIX sisteminde bir thread’in
signal fonksiyonunu kendisinin set etmesi diye bir kavram yoktur. signal fonksiyonu yine
signal ya da sigaction fonksiyonu ile process temelinde set edilir. pthread_kill
yalnızca process temelinde set edilmiş signal fonksiyonunu hangi thread’in ele alıp
işleyeceğini belirleyerek signal’ı gönderir.
int pthread_kill(pthread_t thread, int signo);
Görüldüğü gibi fonksiyonun birinci parametresi thread’in ID değeri, ikinci parametresi
yollanan signal numarasıdır. Örneğin:
pthread_kill(tid, SIGUSR1);
Burada;
1) SIG_USR1 signal ya da sigaction fonksiyonu ile set edilmemişse process sonlanır.
2) SIG_USR1 signal’ı set edilmişse ve signal’ın gönderildiği thread bu signal’a açıksa
thread signal’ı işler.
3) kill ile aynı işlem yapılsaydı signal’ı hangi thread’in işleyeceği ayrıca yukarıda anlatılan
yöntem kullanılarak belirlenmeliydi.
Ayrıca pthread_kill ile başka bir process’in thread’ine signal gönderilmez. Çünkü thread’in
ID değeri process’e özgü bir değerdir. Başka process’lere signal yine kill ile gönderilmek
zorundadır.
Thread’lerin Senkronizasyonu
POSIX sistemlerinde thread’lerin senkronizasyonu için çeşitli yöntemler kullanılmaktadır. Bu
yöntemlerden bazıları farklı process’lerin thread’leri arasında kullanılabilirken, bazıları aynı
process’in thread’leri arasında kullanılabilmektedir.