Linux多線程編程三(互斥鎖)

        在線程實際運行過程當中,咱們常常須要多個線程保持同步。這時能夠用互斥鎖來完成任務;互斥鎖的使用過程當中,主要有pthread_mutex_init,pthread_mutex_destory,pthread_mutex_lock,pthread_mutex_unlock這幾個函數以完成鎖的初始化,鎖的銷燬,上鎖和釋放鎖操做。程序員

一,鎖的建立函數

     鎖能夠被動態或靜態建立,能夠用宏PTHREAD_MUTEX_INITIALIZER來靜態的初始化鎖,採用這種方式比較容易理解,互斥鎖是pthread_mutex_t的結構體,而這個宏是一個結構常量,以下能夠完成靜態的初始化鎖:測試

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;spa

    另外鎖能夠用pthread_mutex_init函數動態的建立,函數原型以下:線程

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr)orm

二,鎖的屬性對象

    互斥鎖屬性能夠由pthread_mutexattr_init(pthread_mutexattr_t *mattr);來初始化,而後能夠調用其餘的屬性設置方法來設置其屬性;遞歸

    互斥鎖的範圍:能夠指定是該進程與其餘進程的同步仍是同一進程內不一樣的線程之間的同步。能夠設置爲隊列

PTHREAD_PROCESS_SHARE和PTHREAD_PROCESS_PRIVATE  進程

     默認是後者,表示進程內使用鎖。

     可使用int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared)

pthread_mutexattr_getshared(pthread_mutexattr_t *mattr,int *pshared)

用來設置與獲取鎖的範圍;

    互斥鎖的類型有如下幾個取值空間:      

    PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當一個線程加鎖之後,其他請求鎖的線程將造成一個等待隊列,並在解鎖後按優先級得到鎖。這種鎖策略保證了資源分配的公平性。

 PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,容許同一個線程對同一個鎖成功得到屢次,並經過屢次unlock解鎖。若是是不一樣線程請求,則在加鎖線程解鎖時從新競爭。

 PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,若是同一個線程請求同一個鎖,則返回EDEADLK,不然與PTHREAD_MUTEX_TIMED_NP類型動做相同。這樣就保證當不容許屢次加鎖時不會出現最簡單狀況下的死鎖。

 PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動做最簡單的鎖類型,僅等待解鎖後從新競爭。

能夠用

pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type)

pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type)

獲取或設置鎖的類型。

三,鎖的釋放

函數原型:

     int pthread_mutex_destroy(pthread_mutex_t *mp); 

     調用pthread_mutex_destory以後,能夠釋放鎖佔用的資源,但這有一個前提上鎖當前是沒有被鎖的狀態。

返回值:

pthread_mutex_destroy() 在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。

EINVAL: mp 指定的值不會引用已初始化的互斥鎖對象。

四,鎖操做

    對鎖的操做主要包括加鎖 pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個。

定互斥鎖

函數原型:

int pthread_mutex_lock(pthread_mutex_t *mutex); 

函數說明:

當 pthread_mutex_lock() 返回時,該互斥鎖已被鎖定。調用線程是該互斥鎖的屬主。若是該互斥鎖已被另外一個線程鎖定和擁有,則調用線程將阻塞,直到該互斥鎖變爲可用爲止。

返回值:

pthread_mutex_lock() 在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。

EAGAIN:因爲已超出了互斥鎖遞歸鎖定的最大次數,所以沒法獲取該互斥鎖。

EDEADLK:當前線程已經擁有互斥鎖。

解除鎖定互斥鎖

函數原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

函數說明:pthread_mutex_unlock() 可釋放 mutex 引用的互斥鎖對象。互斥鎖的釋放方式取決於互斥鎖的類型屬性。若是調用 pthread_mutex_unlock() 時有多個線程被 mutex 對象阻塞,則互斥鎖變爲可用時調度策略可肯定獲取該互斥鎖的線程。對於 PTHREAD_MUTEX_RECURSIVE 類型的互斥鎖,當計數達到零而且調用線程再也不對該互斥鎖進行任何鎖定時,該互斥鎖將變爲可用。

返回值:pthread_mutex_unlock() 在成功完成以後會返回零。

其餘任何返回值都表示出現了錯誤。若是出現如下狀況,該函數將失敗並返回對應的值。

EPERM :當前線程不擁有互斥鎖。

使用非阻塞互斥鎖鎖定

函數原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex); 

函數說明:pthread_mutex_trylock() 是 pthread_mutex_lock() 的非阻塞版本。若是 mutex 所引用的互斥對象當前被任何線程(包括當前線程)鎖定,則將當即返回該調用。不然,該互斥鎖將處於鎖定狀態,調用線程是其屬主。

返回值:pthread_mutex_trylock() 在成功完成以後會返回零。其餘任何返回值都表示出現了錯誤。若是出現如下任一狀況,該函數將失敗並返回對應的值。

EBUSY :

因爲 mutex 所指向的互斥鎖已鎖定,所以沒法獲取該互斥鎖。

EAGAIN:描述:

因爲已超出了 mutex 的遞歸鎖定最大次數,所以沒法獲取該互斥鎖。


五,鎖的使用

互斥鎖用來保證一段時間內只有一個線程在執行一段代碼。必要性顯而易見:假設各個線程向同一個文件順序寫入數據,最後獲得的結果必定是災難性的。

咱們先看下面一段代碼。這是一個讀/寫程序,它們公用一個緩衝區,而且咱們假定一個緩衝區只能保存一條信息。即緩衝區只有兩個狀態:有信息或沒有信息。

void reader_function ( void );

void writer_function ( void );

char buffer;

int buffer_has_item=0;

pthread_mutex_t mutex;

struct timespec delay;

void main ( void ){

 pthread_t reader;

 /* 定義延遲時間*/

 delay.tv_sec = 2;

 delay.tv_nec = 0;

 /* 用默認屬性初始化一個互斥鎖對象*/

 pthread_mutex_init (&mutex,NULL);

pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);

 writer_function( );

}

void writer_function (void){

 while(1){

  /* 鎖定互斥鎖*/

  pthread_mutex_lock (&mutex);

  if (buffer_has_item==0){

   buffer=make_new_item( );

   buffer_has_item=1;

  }

  /* 打開互斥鎖*/

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

void reader_function(void){

 while(1){

  pthread_mutex_lock(&mutex);

  if(buffer_has_item==1){

   consume_item(buffer);

   buffer_has_item=0;

  }

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

程序說明:

       這裏聲明瞭互斥鎖變量mutex,結構pthread_mutex_t爲不公開的數據類型,其中包含一個系統分配的屬性對象。函數pthread_mutex_init用來生成一個互斥鎖。NULL參數代表使用默認屬性。若是須要聲明特定屬性的互斥鎖,須調用函數pthread_mutexattr_init。

      pthread_mutex_lock聲明開始用互斥鎖上鎖,此後的代碼直至調用pthread_mutex_unlock爲止,均被上鎖,即同一時間只能被一個線程調用執行。當一個線程執行到pthread_mutex_lock處時,若是該鎖此時被另外一個線程使用,那此線程被阻塞,即程序將等待到另外一個線程釋放此互斥鎖。在上面的例子中,咱們使用了pthread_delay_np函數,讓線程睡眠一段時間,就是爲了防止一個線程始終佔據此函數。

六,死鎖

       須要提出的是在使用互斥鎖的過程當中頗有可能會出現死鎖:兩個線程試圖同時佔用兩個資源,並按不一樣的次序鎖定相應的互斥鎖,例如兩個線程都須要鎖定互斥鎖1和互斥鎖2,a線程先鎖定互斥鎖1,b線程先鎖定互斥鎖2,這時就出現了死鎖。        有兩個方法:

       一、咱們可使用函數pthread_mutex_trylock,它是函數pthread_mutex_lock的非阻塞版本,若是此時互斥量沒有被上鎖,那麼pthread_mutex_trylock將會返回0,並會對該互斥量上鎖。若是互斥量已經被上鎖,那麼會馬上返回EBUSY,返回相應的信息,程序員能夠針對死鎖作出相應的處理。

       二、調用pthread_mutexattr_init()來建立該互斥量,而後調用pthread_mutexattr_settype來設置屬性。

一、快速型。這種類型也是默認的類型。 

二、遞歸型。若是遇到咱們上面所提到的死鎖狀況,同一線程循環給互斥量上鎖,那麼系統將會知道該上鎖行爲來自同一線程,那麼就會贊成線程給該互斥量上鎖。 

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

七,總結

1.對共享資源操做前必定要得到鎖。

2.完成操做之後必定要釋放鎖。

3.儘可能短期地佔用鎖。

4.若是有多鎖, 如得到順序是ABC連環扣,釋放順序也應該是ABC。

5.線程錯誤返回時應該釋放它所得到的鎖。

相關文章
相關標籤/搜索