Thread Nedir ?

 


 Thread Nedir ?

 

Threadler ya da Türkçe tabir ile "iş parçacıkları" bir uygulamanın (process) iş yapan birimleridir. Bir uygulamada en az bir adet thread olması gerekir. Bu mecburi thread genellikle "main thread" olarak isimlendirilir ve bu thread sonlandığında normal şartlarda uygulamanın sonlaması beklenir (tam burada foreground ve background thread kavramlarını not alın, bu yazıdan sonra araştrın). Threadler işletim sistemi tarafından önceliklerine göre belirli bir süre çalıştırılıp, dondurulurlar. Her bir thread için ayrılan süre ise "time slice" olarak isimlendiriliyor. Bu işlem çok hızlı gerçekleştiği için biz birçok uygulamanın aslında aynı anda iş yaptığını düşünürüz. Fakat aynı anda yapılan iş sayısı elimizdeki işlemci çekirdekleri sayısı kadardır.

 

Thread Oluşturma

 

Yeni bir thread oluşturmak için pthread_create fonksiyonu kullanılır.

Thread fonksiyonlarının kullanımı için pthread.h başlık dosyası include edilmelidir.

Threadler ana program ile aynı adresleme alanını ve aynı file descriptor'ları kullanırlar. 

Pthread kütüphanesi aynı zamanda senkronizasyon işlemleri için gerekli mutex ve conditional işlemleri için gerekli desteği de içermektedir.

thread parametresi pthread_t türünde olup önceden tanımlanması gerekir. Oluşan thread'e bu referansla her zaman erişilebilecektir.

attr parametresi thread spesifik olarak pthread_attr_ ile başlayan fonksiyonlarla ayarlanmış, scheduling policy, stack büyüklüğü, detach policy gibi kuralları gösterir.

start_routine thread tarafından çalıştırılacak olan fonksiyonu gösterir.

arg ise thread tarafından çalıştırılacak fonksiyona geçirilecek void*'a cast edilmiş genel bir veri yapısını göstermektedir.

 

#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("Hello from thread %s\n", name);
    }
    printf("Thread %s done...\n", name);
    return NULL;
}

int main(void) {
    pthread_t th1, th2;
    pthread_create(&th1, NULL, worker, "A");
    pthread_create(&th2, NULL, worker, "B");
    sleep(5);
    printf("Exiting from main program\n");
    return 0;
}
 // Kaynak : https://medium.com/@gokhansengun/semaphore-mutex-ve-spinlock-nedir-ve-ne-i%C5%9Fe-yarar-ba552a17c03

Farklı prosesler arasında ya da aynı proses içinde yer alan Threadler arasında zaman zaman senkronizasyon ihtiyacı duyulur çünkü bu Thread’ler görevlerini yerine getirebilmek için işletim sistemi tarafından sağlanan veya prosesin kendisinin tuttuğu paylaşılan bir kaynağa erişmek isterler. Örnek olarak Thread’lerin bir log dosyasına veya ekrana log yazmak istedikleri durumu ele alalım. İki Thread aynı anda log dosyasına yazmaya çalışırsa dosyaya yazılan loglar birbirine karışacak ve okunmaz hale gelecektir. Burada Thread’leri log dosyasına yazma konusunda sıraya sokacak bir mekanizmaya ihtiyaç vardır.

 

Mutex (MUTual EXclusion)’ler tam da bu mekanizmayı sağlamak için tasarlanmıştır. Mutex’ler uygulamanın yazıldığı dil ve Runtime tarafından sağlanan basit veri yapılarıdır. Farklı Thread’ler tarafından paylaşılan her bir kaynak için kaynağa olan erişimi düzenlemek üzere bir Mutex yaratılır. Paylaşılan kaynağa erişim yapılan kod bölgesi Critical Section olarak adlandırılır. Kaynakla işi olan Thread, Mutex'in sahipliğini almaya (Acquire) çalışır. Mutex o anda başka bir Thread tarafından tutulmuyorsa Thread Mutex'i alır, Critical Section'a girerek ilgili kaynağı kullanır. Diğer durumda, yani Mutex o anda başka bir Thread tarafından kullanılıyor ise, ikinci Thread işlemci tarafından beklemeye alınır. Mutex'i tutan Thread Critical Section'ı bitirip Mutex'i bırakırken, halihazırda Mutex'in bırakılmasını bekleyen Thread uyandırılır ve Mutex'in sahipliğini alarak Critical Section'a girer ve paylaşılan kaynağa erişim sağlar.

 

Semaphore’lar bazı platformlarda Binary ve Counting olarak ikiye ayrılsa da temel olarak bir ya da daha fazla sayıda bulunan kıt kaynağın birçok kullanıcı tarafından sırayla kullanılmasını sağlarlar.

 

Basit bir örnek olarak 4 araç kapasiteli bir otopark otomasyon sistemi kullanılabilir. Bu sistemde giriş ve çıkış kapısını kontrol eden iki Thread’li uygulamada Semaphore Count’u 4 verilmiş bir Counting Semaphore kullanılmış olsun. Otopark boşken, giriş kapısını yöneten Thread Semaphore’un Count’u 4 olduğundan ilk 4 araba için Semaphore’ları alarak kapıyı 4 kere açacak, sonraki araçlar için ise Semaphore boş kaldığından Semaphore dolana kadar işlemci tarafından uyutulacaktır. Çıkış kapısından araçlar çıktıkça (yani Semaphore Release edildikçe) giriş kapısını kontrol eden Thread işlemci tarafından uyandırılacak, Semaphore’u tekrar alabilecek ve çıkan araç kadar yeni araç girişine izin verecektir.

 

Bu noktada benzer amaçlar (Critical Section oluşturmak) için kullanılabilecek Mutex ile Count'u 1 olan Binary Semaphore arasındaki farkı da açıklamak gerekir. Mutex'ler Critical Section yönetimi sağlamak üzere tasarlandığı için kendilerini Release eden Thread'in alan Thread olmasını gerekli tutmaktadır, Semaphore'da ise böyle bir kısıtlama yoktur hatta birçok durumda Semaphore'u bırakan Thread alan Thread'den farklıdır.

 

Özetlemek gerekirse Semaphore’lar aralarında sinyalleşme gereken Producer-Consumer (Üretici-Tüketici) senaryolarında sinyalleşme açısından muhteşem bir çözüm sunmaktadır.

 

Otopark örneğinde kapıyı açan Thread’in Semaphore dolu iken Semaphore’u almak istediğinde işlemci tarafından uyutulduğunu söylemiştik. İşlemcinin ilgili Thread’in bütün state’ini saklaması ve uyandığında tekrar geri getirmesi (Context-Switch) çok maliyetli bir işlem olduğu için Semaphore’un çabucak bırakılacağının tahmin edildiği senaryolarda Thread uyutulmaz, bir süre canlı tutularak Semaphore bırakılana kadar birkaç boş tur (Busy Waiting) attırılır. Bu tür Semaphore’lara Spinlock Semaphore denir ve düşük seviyeli işlemlerde (Device Driver, Linux Kernel, vb) sıklıkla etkili bir şekilde kullanılırlar. Spinlock Semaphore’da Thread belirli bir süre beklediği Semaphore’u alamazsa normal bir Thread gibi işlemci tarafından beklemeye alınır. Tahmin edebileceğiniz gibi Spinlock Semaphore’lar Multi-Core işlemcilerde anlamlı hale gelirler.

 

Bu flood sonunda umarım multithread bir uygulamada aynı log dosyasına aşırı loglamanın uygulama performansını neden kötü etkilediği anlaşılmıştır.

 

Yorum Gönderme

0 Yorumlar