Üye Kayıt Üye Giriş

Mutex Kullanımı


Mutex Kullanımı

Mutex neredeyse her türlü işletim sisteminde olan bir senkronizasyon nesnesidir. Mutex bir
thread tarafından ele geçirilir. Mutex’i ele geçiren thread mutex’i istediği zaman serbest
bırakabilmektedir. Genellikle sistemlerde bir thread mutex’i ele geçirdiği zaman başka bir
thread bu mutex’i ele geçirmeye çalışınca bloke olur. Mutex’in sahibi olan thread mutex
nesnesini bırakana kadar thread bekler. Bazı sistemlerde (örneğin Win32 sistemlerinde)
mutex nesnesi aynı thread tarafından birden fazla kez ele geçirilmektedir. Bu durumda
thread’in mutex’i bırakması için yine ele geçirme sayısı kadar bırakma işlemi yapması
gerekir. Şüphesiz böyle sistemlerde mutex nesnesi içerisinde bir sayaç bulundurulmaktadır.

Mutex Nesnesinin Yaratılması
Mutex nesnesinin yaratılması için pthread_mutex_t türünden bir nesne tanımlanır. Mutex
nesnesini birden fazla thread kullanacağına göre bu nesnenin global ya da heap üzerinde
yaratılması uygundur. Mutex nesnesi tanımlandıktan sonra ona ilk değer vermek gerekir. İlk
değer verme işlemi nesneyi tanımlar tanımlamaz PTHREAD_MUTEX_INITIALIZER
makrosu ile ya da pthread_mutex_init fonksiyonu ile yapılabilir. Örneğin;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Bu işlem ilk değer verme biçiminde yapılmalıdır.
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutex_attr_t *attr);
Fonksiyonun birinci parametresi mutex nesnesinin adresi, ikinci parametresi mutex
özelliklerinin değerlerine ilişkin yapının adresidir. Bu parametre NULL olarak geçilebilir. Bu
durumda mutex default özelliklere sahip olur (PTHREAD_MUTEX_INITIALIZER da
default özellikleri belirler).


Mutex Nesnesinin Ele Geçirilmesi ve Bırakılması
Mutex kullanımı oldukça basittir. Bir thread mutex nesnesini pthread_mutex_lock
fonksiyonu ile ele geçirir. Thread mutex nesnesinin pthread_mutex_unlock ile
bırakmaktadır. Mutex kullanılarak tipik bir kritik kod şöyle oluşturulur:
pthread_mutex_lock(...);
// ...
Kritik kod !
// ...
pthread_mutex_unlock(...);

Kritik kod (critical section) yalnızca tek bir akış tarafından işlenecek kod parçası için
kullanılan bir terimdir. Mutex ilk kez pthread_mutex_lock fonksiyonunu çağıran thread
tarafından ele geçirilir. Mutex bir thread tarafından ele geçirilmiş ise bu durumda başka bir
thread pthread_mutex_lock fonksiyonunu aynı mutex nesnesi ile kullanırsa bloke olarak
bekler. Mutex’e sahip olan thread mutex nesnesini bıraktığında beklemekte olan en yüksek
öncelikli thread mutex’in kontrolünü ele alarak mutex’i ele geçirir.
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Örnek Uygulama
Global bir bağlı listenin birden fazla thread tarafından asenkron biçimde kullanıladığını
düşünelim. Bu durumda bir thread bağlı liste işi yaparken diğerinin beklemesi gerekir.


/* listapp.cpp */
#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#include <algorithm>
#include <list>
#define SIZE 10000
using namespace std;
list<int> g_list;
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
void check_error(const char *msg, int result)
{
if (result) {
fprintf(stderr, "%s: %s\n", msg, strerror(result));
exit(EXIT_FAILURE);
}
}
void *Thread1(void *param)
{
for (int i = 0; i < SIZE; ++i) {
for (int k = 0; k < 100000; ++k)
;
pthread_mutex_lock(&g_mutex);
g_list.push_back(i);
pthread_mutex_unlock(&g_mutex);
}
}
void *Thread2(void *param)
{
for (int i = 0; i < SIZE; ++i) {
pthread_mutex_lock(&g_mutex);
g_list.push_back(-i);
pthread_mutex_unlock(&g_mutex);
for (int k = 0; k < 100000; ++k)
;
}
}
int main(void)
{
int result;
pthread_t tid1, tid2;
result = pthread_create(&tid1, NULL, Thread1, NULL);
check_error("pthread_create", result);
result = pthread_create(&tid2, NULL, Thread2, NULL);
check_error("pthread_create", result);
result = pthread_join(tid1, NULL);
check_error("pthread_join", result);
result = pthread_join(tid2, NULL);
check_error("pthread_join", result);
copy(g_list.begin(), g_list.end(), ostream_iterator<int>(cout, " "));
return 0;
}


Bazı durumlarda programcı mutex nesnesi eğer başka bir thread tarafından ele geçirilmediyse
ele geçirmek ister. Yani mutex’i başka bir thread ele geçirdiyse bloke olmak istemez. Bunun
için pthread_mutex_trylock fonksiyonu kullanılmaktadır.
int pthread_mutex_trylock(pthread_mutex_t *mutex);
Fonksiyon eğer mutex nesnesi boşta ise nesneyi ele geçirir ve başarı ile geri döner. Eğer
mutex nesnesi başka bir thread tarafından ele geçirilmişse fonksiyon bloke olmaz EBUSY
error kodu ile geri döner.
Anahtar notlar: Win32 sistemlerinde mutex nesnesi CreateMutex API fonksiyonu ile yaratılır.
Mutex nesnesini yaratan kişi tarafından ele geçirilip geçirilmeyeceği bu fonksiyonun
parametresinde belirlenir. Bu sistemlerde mutex nesnesini ele geçirmek için
WaitForSingleObject fonksiyonuna bırakılır. Bu fonskiyon adeta pthred_mutex_lock gibi
işlem yapar. Mutex nesnesi ele geçirilmişken başka bir thread WaitForSingleObject
uygularsa blokede kalır, thread mutex fonksiyonunu ReleaseMutex fonksiyonu ile bırakılır.
ReleaseMutex pthread_mutex_unlock gibi işlem yapar. POSIX sistemlerinde mutex’e sahip
olan thread’in bir kere daha pthread_mutex_lock uygulaması geçerli bir işlem değildir. Oysa
Win32 sistemlerinde mutex nesnesine sahip olan thread birden fazla WaitForSingleObject
uygulayabilir. Fakat nesneyi geri bırakmak için o sayıda ReleaseMutex yapmak gerekir.


Mutex Nesnesinin Process’ler Arasında Kullanılması
Mutex nesnesinin process’ler arasında kullanılabilmesi için paylaşılmış bir alanda bulunması
gerekir. Ancak öncelikle mutex nesnesini yaratırken bunun process’ler arasında ortak
kullanılacağı belirtilmelidir. Bilindiği gibi pthread_mutex_init fonksiyonu ile mutex’e ilk
değer verirken bu fonksiyon parametre olarak bir özellik almaktadır. Bu özellik bilgisi
pthread_mutexattr_t türü ile temsil edilir. Bunun için önce pthread_mutexattr_init fonksiyonu
çağırılır, sonra işlem sonunda pthread_mutexattr_destroy yapılır. Özelliği set eden tek bir
fonksiyon vardır. Bu fonksiyon da pthread_mutexattr_setpshared fonksiyonudur.
Bu fonksiyonun ikinci parametresinde paylaşım için PTHREAD_PROCESS_SHARED girilir.
Default olarak eğer mutex nesnesi PTHREAD_MUTEX_INITIALIZER ile ilk değer verilerek
tanımlanmışsa ya da pthread_mutex_init fonksiyonunda özellik parametresine NULL
geçilerek oluşturulmuşsa nesne process’ler arasında paylaşılamaz.
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);


Özetle, process’ler arasında paylaşılan bir mutex nesnesi yaratmak için şunlar yapılmalıdır:
1) Her iki process’te de mutex nesnesi paylaşılan bellekte yaratılır. Bunu sağlamak için her
iki programda da pthread_mutex_t türünden global bir gösterici alınır. Sonra shmget
fonksiyonu ile paylaşılan bellekte mutex nesnesi için ortak alan tahsis edilir.
2) Mutex nesnesinin özellikleri için pthread_mutexattr_t türünden bir nesne tanımlanır. Bu
nesne için önce pthread_mutexattr_init fonksiyonu çağırılır. Sonra
pthread_mutexattr_setpshared fonksiyonu çağırılarak mutex’in process’ler arasında
paylaşılacağı bilgisi verilir.
3) pthread_mutex_init fonskiyonu ile paylaşılan alanda yaratılmış olan mutex yaratılmış olan
özellikle set edilir.
4) Bu noktada artık özellik nesnesi pthread_mutexattr_destroy ile geri bırakılabilir.
5) Artık mutex işlemleri yapılır. İşlemin sonunda mutex nesnesi ve paylaşılan bellek alanı
geri bırakılır.
Anahtar notlar: Linux sistemlerinde pthread_mutexattr_setpshared fonksiyonu yanı sıra
pthread_mutexattr_getkind_np fonksiyonu kullanılmaktadır.

Bilgisayar Dershanesi Ders Sahibi;
Bilgisayar Dershanesi

Yorumlar

Yorum Yapabilmek İçin Üye Girişi Yapmanız Gerekmektedir.

ETİKETLER