Linux’ta, POSIX iş parçacığı (pthread) kitaplığını kullanarak C/C++’da iş parçacıkları oluşturabilir ve yönetebilirsiniz. Diğer işletim sistemlerinden farklı olarak, Linux’ta bir iş parçacığı ile işlem arasında çok az fark vardır. Bu nedenle Linux, iş parçacıklarını genellikle hafif işlemler olarak adlandırır.
pthread kitaplığını kullanarak iş parçacıkları oluşturabilir, bunların sona ermesini bekleyebilir ve açıkça sonlandırabilirsiniz.
Linux’ta İş Parçacığı Kullanımının Tarihi
Linux sürüm 2.6’dan önce, ana iş parçacığı uygulaması LinuxThreads idi. Bu uygulama, performans ve senkronizasyon işlemleri açısından önemli sınırlara sahipti. Çalışabilecek maksimum iş parçacığı sayısına ilişkin bir sınır, onları 1000’lerle sınırladı.
2003 yılında, IBM ve RedHat’tan geliştiricilerin liderliğindeki bir ekip, Native POSIX Thread Library (NPTL) projesini kullanılabilir hale getirmeyi başardı. Linux’ta Java Sanal Makinesi ile ilgili performans sorunlarını çözmek için ilk olarak RedHat Enterprise sürüm 3’te tanıtıldı. Bugün, GNU C kütüphanesi her iki iş parçacığı mekanizmasının uygulamalarını içerir.
Bunların hiçbiri, bir Sanal Makinenin tamamen kullanıcı modunda yöneteceği ve çalıştıracağı yeşil iş parçacıklarının bir uygulaması değildir. pthread kitaplığını kullandığınızda, çekirdek bir program her başladığında bir iş parçacığı oluşturur.
Çalışan herhangi bir işlem için iş parçacığına özgü bilgileri /proc/<PID>/task altındaki dosyalarda bulabilirsiniz . Bu, procfs Linux standardı kapsamında işlem bilgileri için standart konumdur . Single-thread uygulamalar için bu dizin altında PID ile aynı değere sahip bir görev kaydı olduğu görülecektir.
Threadlerin Çalışma Mantığı
İş parçacıkları, işletim sisteminde şu anda çalışan işlemler gibidir. Tek işlemcili sistemlerde (örn. mikrodenetleyiciler), işletim sistemi çekirdeği iş parçacıklarını simüle eder. Bu, işlemlerin dilimleme yoluyla aynı anda çalışmasına izin verir.
Tek çekirdekli bir işletim sistemi, bir seferde gerçekten yalnızca bir işlem çalıştırabilir. Ancak çok çekirdekli veya çok işlemcili sistemlerde bu işlemler aynı anda çalışabilir.
C’de Konu Oluşturma
Yeni bir ileti dizisi oluşturmak için pthread_create işlevini kullanabilirsiniz . pthread.h başlık dosyası, diğer iş parçacığıyla ilgili işlevlerle birlikte kendi imza tanımını içerir . Konular, ana programla aynı adres alanını ve dosya tanımlayıcıları kullanır.
pthread kütüphanesi senkronizasyon işlemleri için gerekli olan mutex ve koşullu işlemler için gerekli desteği de içermektedir.
pthread kitaplığının işlevlerini kullanırken, derleyicinin pthread kitaplığını yürütülebilir dosyanıza bağlamasını sağlamalısınız . Gerekirse, derleyiciye -l seçeneğini kullanarak kitaplığa bağlanma talimatı verebilirsiniz :
gcc -o test test_thread.c -lpthread
pthread_create işlevi aşağıdaki imzaya sahiptir:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
Prosedür başarılı olursa 0 döndürür. Bir sorun varsa, sıfır olmayan bir hata kodu döndürür. Yukarıdaki işlev imzasında:
thread parametresi pthread_t türündedir . Oluşturulan iş parçacığına bu referansla her zaman erişilebilir olacaktır.
attr parametresi, özel davranış belirlemenizi sağlar . Bu değeri ayarlamak için pthread_attr_ ile başlayan diziye özgü işlevler kullanabilirsiniz. Olası özelleştirmeler, zamanlama ilkesi, yığın boyutu ve ayırma ilkesidir.
start_routine iş parçacığının çalışacağı işlevi belirtir.
arg , iş parçacığı tarafından işleve iletilen genel bir veri yapısını temsil eder.
İşte örnek bir uygulama:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *worker(void *data)
{
char *name = (char*)data;
for (int i = 0; i < 120; i++)
{
usleep(50000);
printf(“Hi from thread name = %sn”, name);
}
printf(“Thread %s done!n”, name);
return NULL;
}
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, “X”);
pthread_create(&th2, NULL, worker, “Y”);
sleep(5);
printf(“Exiting from main programn”);
return 0;
}
Aynı anda çalışan iki iş parçacığını gösteren program çıktısı
Konu Tipleri
Bir uygulamadaki main() işlevinden bir iş parçacığı döndüğünde , tüm iş parçacıkları sona erer ve sistem, programın kullandığı tüm kaynakları serbest bırakır. Aynı şekilde, herhangi bir diziden exit() gibi bir komutla çıkarken , programınız tüm dizileri sonlandıracaktır.
pthread_join işleviyle bunun yerine bir iş parçacığının sonlanmasını bekleyebilirsiniz. Bu işlevi kullanan iş parçacığı, beklenen iş parçacığı sona erene kadar engelleyecektir. Birleştirilebilir iş parçacıklarının sonlandırılması, CPU tarafından planlanmaması, hatta ptread_join ile birleştirilememesi gibi durumlarda bile sistemden kullandıkları kaynaklar geri dönmez .
Bazen pthread_join ile katılmanın mantıklı olmadığı durumlar vardır; Örneğin, iş parçacığının ne zaman biteceğini tahmin etmek imkansızsa. Bu durumda thread’in döndüğü noktada sistemin tüm kaynakları otomatik olarak döndürmesini sağlayabilirsiniz.
Bunu başarmak için ilgili konuları DETACHED statüsünde başlatmalısınız. Bir iş parçacığını başlatırken, DETACH durumu bir iş parçacığı öznitelik değerleri aracılığıyla veya pthread_detach işleviyle ayarlanabilir :
int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int pthread_detach(pthread_t thread);
İşte pthread_join() kullanımının bir örneği. İlk programdaki ana işlevi aşağıdakiyle değiştirin:
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, “X”);
pthread_create(&th2, NULL, worker, “Y”);
sleep(5);
printf(“exiting from main programn”);
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}
Programı derleyip çalıştırdığınızda çıktınız şu şekilde olacaktır:
Hi from thread Y
Hi from thread X
Hi from thread Y
…
Hi from thread Y
exiting from main program
Hi from thread X
…
Hi from thread X
Thread X done!
Hi from thread Y
Thread Y done!
Konu Sonlandırma
İlgili pthread_t kimliğini ileterek pthread_cancel çağrısıyla bir diziyi iptal edebilirsiniz :
int pthread_cancel(pthread_t thread);
Bunu aşağıdaki kodda çalışırken görebilirsiniz. Yine, yalnızca ana işlev farklıdır:
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, “X”);
pthread_create(&th2, NULL, worker, “Y”);
sleep(1);
printf(“====> Cancelling Thread Y!!n”);
pthread_cancel(th2);
usleep(100000);
printf(“====> Cancelling Thread X!n”);
pthread_cancel(th1);
printf(“exiting from main programn”);
return 0;
}
Çalışan ve ardından iptal edilen iş parçacıklarını gösteren bir programdan çıktı
Konular Neden Oluşturulur?
İşletim sistemleri her zaman bir veya daha fazla CPU’da kendi oluşturduğu bir listeden veya kullanıcı tarafından oluşturulan bir dizi listesinden iş parçacığı çalıştırmaya çalışır. Bazı iş parçacıkları donanımdan bir giriş/çıkış sinyali bekledikleri için çalışamazlar. Ayrıca gönüllü olarak bekliyor, başka bir ileti dizisinden yanıt bekliyor veya başka bir ileti dizisi onları engelliyor olabilir.
pthread kullanarak oluşturduğunuz thread’lere ayırdığınız kaynakları ayarlayabilirsiniz. Bu, özel bir zamanlama politikası olabilir veya istenirse FIFO veya Round-robin gibi zamanlama algoritmalarını seçebilirsiniz.