Linux之線程同步-互斥量

來源:http://www.yuanma.org/data/2006/0721/article_1195.htm


        進行多線程編程,最頭疼的就是那些共享的數據。因爲你無法知道哪個線程會在哪個時候對它進行操作,你也無法得知那個線程會先運行,哪個線程會後運行。下面介紹一些技術,通過他們,你會合理安排你的線程之間對資源的競爭。

l         互斥體Mutex

l         信號燈Semophore

l         條件變量Conditions

先說一下互斥量。

        什麼時候會用上互斥量了?比如你現在有一全局鏈表,你有幾個工作線程。每一個線程從該鏈表中取出頭節點,然後對該頭節點進行處理。比如現在線程1正在取出頭節點,他的操作如下:

Item * p =queue_list;

Queue_list=queue_list->next;

Process_job(p);

Free(p);

       當線程1處理完第一步,也就是Item *p=queue_list後,這時候系統停止線程1的運行,改而運行線程2。線程2照樣取出頭節點,然後進行處理,最後釋放了該節點。過了段時間,線程1重新得到運行。而這個時候,其實p所指向的節點已經被線程2釋放掉,而線程1對此毫無知曉。他會接着運行process_job(p)。而這將導致無法預料的後果!

       對於這種情況,系統給我們提供了互斥量。你在取出頭節點前必須要等待互斥量,如果此時有其他線程已經獲得該互斥量,那麼線程將會阻塞在這個地方。只有等到其他線程釋放掉該互斥量後,你的線程纔有可能得到該互斥量。爲什麼是可能了?因爲可能此時有不止你一個線程在等候該互斥量,而系統無法保證你的線程將會優先運行。

       互斥量的類型爲pthread_mutex_t。你可以聲明多個互斥量。在聲明該變量後,你需要調用pthread_mutex_init()來創建該變量。pthread_mutex_init的格式如下:

int  pthread_mutex_init(pthread_mutex_t  *mutex,  const  pthread_mutex-attr_t *mutexattr);

        第一個參數,mutext,也就是你之前聲明的那個互斥量,第二個參數爲該互斥量的屬性。這個將在後面詳細討論。

        在創建該互斥量之後,你便可以使用它了。要得到互斥量,你需要調用下面的函數:

int pthread_mutex_lock(pthread_mutex_t *mutex);

        該函數用來給互斥量上鎖,也就是我們前面所說的等待操作。互斥量一旦被上鎖後,其他線程如果想給該互斥量上鎖,那麼就會阻塞在這個操作上。如果在此之前該互斥量已經被其他線程上鎖,那麼該操作將會一直阻塞在這個地方,直到獲得該鎖爲止。

        在得到互斥量後,你就可以進入關鍵代碼區了。

同樣,在操作完成後,你必須調用下面的函數來給互斥量解鎖,也就是前面所說的釋放。這樣其他等待該鎖的線程纔有機會獲得該鎖,否則其他線程將會永遠阻塞。

int pthread_mutex_unlock(pthread_mutex_t *mutex);

下面給出一個簡單的例子:

#include <malloc.h>

#include <pthread.h>

struct job {
/* Link field for linked list. */

struct job* next;

/* Other fields describing work to be done... */
};

/* A linked list of pending jobs. */

struct job* job_queue;

/* A mutex protecting job_queue. */

pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;

/* Process queued jobs until the queue is empty. */

void* thread_function (void* arg)

{

while (1) {

struct job* next_job;

/* Lock the mutex on the job queue. */

pthread_mutex_lock (&job_queue_mutex);

/* Now it’s safe to check if the queue is empty. */

if (job_queue == NULL)

next_job = NULL;

else {

/* Get the next available job. */

next_job = job_queue;

/* Remove this job from the list. */

job_queue = job_queue->next;

}

/* Unlock the mutex on the job queue because we’re done with the

queue for now. */

pthread_mutex_unlock (&job_queue_mutex);

/* Was the queue empty? If so, end the thread. */

if (next_job == NULL)

break;

/* Carry out the work. */

process_job (next_job);

/* Clean up. */

free (next_job);

}

return NULL;

}

在這個例子中我們使用了下面一條語句:

pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;

       他的作用和調用pthread_mutex_init()函數一樣。

 

        如果一個線程已經給一個互斥量上鎖了,後來在操作的過程中又再次調用了該上鎖的操作,那麼該線程將會無限阻塞在這個地方,從而導致死鎖。怎麼變了?這就需要我們之前所提到的互斥量的屬性。

互斥量分爲下面三種:

l         快速型。這種類型也是默認的類型。該線程的行爲正如上面所說的。

l         遞歸型。如果遇到我們上面所提到的死鎖情況,同一線程循環給互斥量上鎖,那麼系統將會知道該上鎖行爲來自同一線程,那麼就會同意線程給該互斥量上鎖。

l         錯誤檢測型。如果該互斥量已經被上鎖,那麼後續的上鎖將會失敗而不會阻塞,pthread_mutex_lock()操作將會返回EDEADLK。

        互斥量的屬性類型爲pthread_mutexattr_t。聲明後調用pthread_mutexattr_init()來創建該互斥量。

        然後調用int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);來設置屬性。

        int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);

        格式如下:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);

第一個參數,attr,就是前面聲明的屬性變量,第二個參數,kind,就是我們要設置的屬性類型。他有下面幾個選項:

l         PTHREAD_MUTEX_FAST_NP

l         PTHREAD_MUTEX_RECURSIVE_NP

l         PTHREAD_MUTEX_ERRORCHECK_NP

下面給出一個使用屬性的簡單過程:

pthread_mutex_t mutex;

pthread_mutexattr_t attr;

pthread_mutexattr_init(&attr);

pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);

pthread_mutex_init(&mutex,&attr);

pthread_mutex_destroy(&attr);

 

前面我們提到在調用pthread_mutex_lock()的時候,如果此時mutex已經被其他線程上鎖,那麼該操作將會一直阻塞在這個地方。如果我們此時不想一直阻塞在這個地方,那麼可以調用下面函數:

pthread_mutex_trylock()

如果此時互斥量沒有被上鎖,那麼pthread_mutex_trylock()將會返回0,並會對該互斥量上鎖。如果互斥量已經被上鎖,那麼會立刻返回EBUSY。




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