POSIX多線程互斥量及其應用

POSIX多線程互斥量及其應用

 

pthread_mutex_t  _mutex = PTHREAD_MUTEX_INITIALIZER;                      

int pthread_mutex_init( pthread_mutex_t  *mutex,  pthread_mutex  attr_t  *attr );

int pthread_mutex_destroy (pthread_mutex_t  *mutex);

 

程序中的互斥量是用pthread_mutex_t類型的變量來表示的,不能拷貝互斥量變量,因爲是用拷貝的互斥量是不確定的。但是可以拷貝指向互斥量的指針,這樣就可以使多個函數或線程共享互斥量來實現同步。

創建和銷燬互斥量:

互斥量一般聲明爲兩種類型,在函數體外、文件範圍內使用聲明爲靜態類型;如果有其他文件使用則聲明爲外部類型。當使用malloc動態分配一個包含互斥量的數據結構時,通常不採用靜態方式(①)初始化一個互斥量,此時應使用pthread_mutex_init(②)來動態初始化靜態類型的互斥量。如果要動態初始化靜態類型的互斥量,則必須保證每個互斥量在使用前被初始化且只能被初始化一次。當初始化一個非缺省屬性的互斥量時,則必須使用動態初始化。

當不再需要一個通過pthread_mutex_init動態初始化的互斥量時,應調用int pthread_mutex_destroy來釋放它。而使用PTHREAD_MUTEX_INITIALIZER宏初始化的互斥量則不需要被釋放。

 

int pthread_mutex_lock    (pthread_mutex_t  *mutex);

int pthread_mutex_trylock  (pthread_mutex_t  *mutex);

int pthread_mutex_unlock  (pthread_mutex_t  *mutex);

 

 

互斥量的加鎖與解鎖:

在最簡單情況下使用互斥量時容易的事情:通過調用pthread_mutex_lock或pthread_mutex_trylock鎖住互斥量,處理共享數據,然後調用pthread_mutex_unlock解鎖互斥量。

當調用線程已經鎖住互斥量之後,就不能再加鎖該互斥量。試圖這樣做的結果可能是返回錯誤(EDEADLK)或者可能陷入“自死鎖”,使線程永遠等待下去。不能解鎖一個已經解鎖的互斥量,也不能解鎖由其他線程鎖住的互斥量。被鎖住的互斥量是屬於加鎖線程的。

 

接下來貼一段使用互斥量版本的鬧鐘例子(運行環境爲ubuntu + Qt):

 

#include <QCoreApplication>

#include <pthread.h>

#include <time.h>

#include "errors.h"

#include <QDebug>

 

 

typedef struct alarm_tag {

    struct alarm_tag    *link;

    int                 seconds;

    time_t              time;   /* seconds from EPOCH */

    char                message[64];

} alarm_t;

 

pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;

alarm_t *alarm_list = NULL;

 

void *alarm_thread (void *arg)

{

    alarm_t *alarm;

    int sleep_time;

    time_t now;

    int status;

 

    while (1) {

        status = pthread_mutex_lock (&alarm_mutex);

        if (status != 0)

            err_abort (status, "Lock mutex");

        alarm = alarm_list;

 

        if (alarm == NULL)

            sleep_time = 1;

        else {

            alarm_list = alarm->link;

            now = time (NULL);

            if (alarm->time <= now)

                sleep_time = 0;

            else

                sleep_time = alarm->time - now;

#ifdef DEBUG

            printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,

                sleep_time, alarm->message);

#endif

            }

 

        status = pthread_mutex_unlock (&alarm_mutex);

        if (status != 0)

            err_abort (status, "Unlock mutex");

        if (sleep_time > 0)

            sleep (sleep_time);

        else

            sched_yield ();

 

        if (alarm != NULL) {

            printf ("(%d) %s\n", alarm->seconds, alarm->message);

            free (alarm);

        }

    }

}

 

int main(int argc, char *argv[])

{

//    QCoreApplication a(argc, argv);

    int status;

        char line[128];

        alarm_t *alarm, **last, *next;

        pthread_t thread;

 

        status = pthread_create (

            &thread, NULL, alarm_thread, NULL);

        if (status != 0)

            err_abort (status, "Create alarm thread");

        while (1) {

            printf ("alarm> ");

            if (fgets (line, sizeof (line), stdin) == NULL) exit (0);

            if (strlen (line) <= 1) continue;

            alarm = (alarm_t*)malloc (sizeof (alarm_t));

            if (alarm == NULL)

                errno_abort ("Allocate alarm");

 

            if (sscanf (line, "%d %64[^\n]",

                &alarm->seconds, alarm->message) < 2) {

                fprintf (stderr, "Bad command\n");

                free (alarm);

            } else {

                status = pthread_mutex_lock (&alarm_mutex);

                if (status != 0)

                    err_abort (status, "Lock mutex");

                alarm->time = time (NULL) + alarm->seconds;

 

                last = &alarm_list;

                next = *last;

                while (next != NULL) {

                    if (next->time >= alarm->time) {

                        alarm->link = next;

                        *last = alarm;

                        break;

                    }

                    last = &next->link;

                    next = next->link;

                }

 

                if (next == NULL) {

                    *last = alarm;

                    alarm->link = NULL;

                }

    #ifdef DEBUG

                printf ("[list: ");

                for (next = alarm_list; next != NULL; next = next->link)

                    printf ("%d(%d)[\"%s\"] ", next->time,

                        next->time - time (NULL), next->message);

                printf ("]\n");

    #endif

                status = pthread_mutex_unlock (&alarm_mutex);

                if (status != 0)

                    err_abort (status, "Unlock mutex");

            }

        }

 

//    return a.exec();

}

 

上面程序是對多線程版本鬧鐘程序的改進。

簡單理下程序結構:

alarm_t結構體包含了一個標準UNIX  time_t類型的絕對時間,表示從UNIX紀元(1970年1月1日 00:00)開始到鬧鈴時的秒數。線程函數依次處理alarm_list 中每個鬧鐘的請求,線程永不停止,當main函數返回時,線程“政蒸發”。如果列表中沒有鬧鐘請求,則線程阻塞自己1秒,解鎖互斥量,以便主線程可以添加新的鬧鐘請求。

在線程睡眠或阻塞之前,總要解鎖互斥量。如果互斥量仍被鎖住,則主線程即就無法向列表中添加請求,這將使程序變成同步工作方式。調用sched_yield則變得不同,現在只需簡單理解爲將處理器交給另一個等待運行的線程。

主函數程序與之前差別不大,主要是講鬧鐘請求排序後加入鬧鐘結構體中。

該改進版本有幾個缺點。雖然與多進程版本和多線程版本相比,該版本佔用的資源很少,但是響應性不夠好。改進的方法是使用條件變量來通知共享數據狀態的變化,之後我們會有說明。

 

如果大家覺得還闊以,歡迎大家留言交流。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章