1 Nisan 2014 Salı

PTHREADS ile MUTEX kullanımı gerekliliği, örnek C++ programı. Pthread ve Mutex Kullanımı Örnek

Mutex (mutual exclusion) yani diğer adı ile karşılıklı dışlama, bellekteki kritik bölgelerde işlem yapılırken threadlerin birbirini dışlaması için kullanılan bir kavramdır. Pthread ile mutex işlemini niye kullanmamız gerektiğine dair örnek vereceğim. Aşağıdaki kod misal bir banka hesabında işlem yapmaktadır. Aslında bu örnek genellikle veritabanı sistemlerinde transaction yapısına örnek vermek için kullanılır. Neyse konuya dönelim. Eğer para çekmek ve para yatırmak için ayrı ayrı threadler oluşturduğumuzu varsayalım. Bir banka hesabına aynı anda para yatırma ve para çekme işlemi yaptığımızda aşağıdaki kod parçacığı normal bir şekilde çalışması lazım.
#include <pthread.h>
#include <iostream>
#include <Windows.h>
#include <conio.h>

using namespace std;

static double vadesizHesap = 1000.00f;


void* ParaYatir(void* miktar)
{

    double para = vadesizHesap;
    Sleep(2000);
    para += *(double *)miktar;
    vadesizHesap = para;

    return NULL;
}

void* ParaCek(void* miktar)
{

    double para = vadesizHesap;
    Sleep(2000);
    para -= *(double *)miktar;
    vadesizHesap = para;

    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_t thread1, thread2;
    double cekilecekMiktar = 0, yatirilacakMiktar = 0;
    cout << endl << "Hesabinizdaki para tutari:" << vadesizHesap;
    cout << endl << "Cekilecek para tutarini giriniz:";
    cin >> cekilecekMiktar;
    cout << endl << "Yatirilacak para tutarini giriniz:";
    cin >> yatirilacakMiktar;
    int sonuc = pthread_create(&thread1, NULL, ParaYatir, (void*)&yatirilacakMiktar);
    if (sonuc) _exit(-1);
    sonuc = pthread_create(&thread2, NULL, ParaCek, (void*)&cekilecekMiktar);
    if (sonuc) _exit(-1);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    cout << endl << "Hesabinizdaki para:" << vadesizHesap;
    cin >> sonuc;
    return 0;
}
Yukarıdaki programa girdileri girdiğimizde oluşan sonuç aşağıdaki gibidir.

Hesabinizdaki para tutari:1000
Cekilecek para tutarini giriniz:100

Yatirilacak para tutarini giriniz:100

Hesabinizdaki para:900

Peki neden böyle oldu? Dikkat ederseniz ParaCek ve ParaYatir fonksiyonları thread olarak çalıştırılıyor. Ve ikiside vadesiz hesabı okuyor. İkiside 1000 TL okudu. Ancak sleep komutu ile bu iki threadde uyku moduna geçti Sleep ile uyutmamın sebebi işletim sistemi her threadi her zaman çalıştırmak zorunda olmadığıdır. Bazen sistemdeki threadler beklemek zorunda kalabilir. Tam bu sırada para yatırma işlemi çalışmaya başladı 1000 TL nin üstüne 100TL daha koyup 1100TL yi belleğe yazdı ve işini bitirdi. Ancak thread2 olan para çekme fonksiyonu hala beklemedeyken bu işlem gerçekleşti. Daha sonra thread2 yani para çekme fonksiyonu kaldığı yerden devam etti. En son okuduğu vadesizHesap(1000TL) 'tan verilen 100 TL yi çekerek 900 TL yaptı ve vadesizHesap verisini bellekte yeniledi. İşte bu tür durumları engellemek için mutex kavramını kullanmak zorundayız. Bu tarz bellekte YAZMA işlemi yapılan bölgelerde farklı threadler aynı anda o bellek bölümüne ulaşmalarını engellemeliyiz. Aşağıdaki kod parçacığı üstteki programın mutex kullanılarak yazılmış halidir. Yazma bölgesine girmeden önce ilk gelen thread(thread1 ya da thread2) mutex'i kilitliyor. Ve aynı anda diğer thread ise mutex bölgesine giriş yapamıyor. Mutex kilidi açıldıktan sonra diğer thread mutex kilidini kendisi için kilitliyor ve işlemini yapıp mutex kilidini açıyor.
#include <pthread.h>
#include <iostream>
#include <Windows.h>
#include <conio.h>

using namespace std;

static double vadesizHesap = 1000.00f;
pthread_mutex_t kilit = PTHREAD_MUTEX_INITIALIZER;

void* ParaYatir(void* miktar)
{
    pthread_mutex_lock(&kilit);
    double para = vadesizHesap;
    Sleep(2000);
    para += *(double *)miktar;
    vadesizHesap = para;
    pthread_mutex_unlock(&kilit);
    return NULL;
}

void* ParaCek(void* miktar)
{
    pthread_mutex_lock(&kilit);
    double para = vadesizHesap;
    Sleep(2000);
    para -= *(double *)miktar;
    vadesizHesap = para;
    pthread_mutex_unlock(&kilit);
    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_t thread1, thread2;
    double cekilecekMiktar = 0, yatirilacakMiktar = 0;
    cout << endl << "Hesabinizdaki para tutari:" << vadesizHesap;
    cout << endl << "Cekilecek para tutarini giriniz:";
    cin >> cekilecekMiktar;
    cout << endl << "Yatirilacak para tutarini giriniz:";
    cin >> yatirilacakMiktar;
    int sonuc = pthread_create(&thread1, NULL, ParaYatir, (void*)&yatirilacakMiktar);
    if (sonuc) _exit(-1);
    sonuc = pthread_create(&thread2, NULL, ParaCek, (void*)&cekilecekMiktar);
    if (sonuc) _exit(-1);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    cout << endl << "Hesabinizdaki para:" << vadesizHesap;
    cin >> sonuc;
    return 0;
}
Programın çıktısı aşağıdaki gibidir. Kolay gelsin..

Hesabinizdaki para tutari:1000
Cekilecek para tutarini giriniz:150

Yatirilacak para tutarini giriniz:100

Hesabinizdaki para:950

1 yorum:

  1. Teşekkür ederim gerçekten kafamda bir soru işareti olarak kalmıştı ek olarak semaphore pthread hakkında da bir açıklama geçebilirseniz sevinirim. Teşekkürler.

    YanıtlaSil