三種專門用於線程同步的機制:POSIX信號量,互斥量和條件變量.
在Linux上信號量API有兩組,壹組是System V IPC信號量,即PV操作,另外就是POSIX信號量,POSIX信號量的名字都是以sem_開頭.
phshared參數指定信號量的類型,若其值為0,就表示這個信號量是當前進程的局部信號量,否則該信號量可以在多個進程之間***享.value值指定信號量的初始值,壹般與下面的sem_wait函數相對應.
其中比較重要的函數sem_wait函數會以原子操作的方式將信號量的值減壹,如果信號量的值為零,則sem_wait將會阻塞,信號量的值可以在sem_init函數中的value初始化;sem_trywait函數是sem_wait的非阻塞版本;sem_post函數將以原子的操作對信號量加壹,當信號量的值大於0時,其他正在調用sem_wait等待信號量的線程將被喚醒.
這些函數成功時返回0,失敗則返回-1並設置errno.
生產者消費者模型:
生產者對應壹個信號量:sem_t producer;
消費者對應壹個信號量:sem_t customer;
sem_init(&producer,2)----生產者擁有資源,可以工作;
sem_init(&customer,0)----消費者沒有資源,阻塞;
在訪問公***資源前對互斥量設置(加鎖),確保同壹時間只有壹個線程訪問數據,在訪問完成後再釋放(解鎖)互斥量.
互斥鎖的運行方式:串行訪問***享資源;
信號量的運行方式:並行訪問***享資源;
互斥量用pthread_mutex_t數據類型表示,在使用互斥量之前,必須使用pthread_mutex_init函數對它進行初始化,註意,使用完畢後需調用pthread_mutex_destroy.
pthread_mutex_init用於初始化互斥鎖,mutexattr用於指定互斥鎖的屬性,若為NULL,則表示默認屬性。除了用這個函數初始化互斥所外,還可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用於銷毀互斥鎖,以釋放占用的內核資源,銷毀壹個已經加鎖的互斥鎖將導致不可預期的後果。
pthread_mutex_lock以原子操作給壹個互斥鎖加鎖。如果目標互斥鎖已經被加鎖,則pthread_mutex_lock則被阻塞,直到該互斥鎖占有者把它給解鎖.
pthread_mutex_trylock和pthread_mutex_lock類似,不過它始終立即返回,而不論被操作的互斥鎖是否加鎖,是pthread_mutex_lock的非阻塞版本.當目標互斥鎖未被加鎖時,pthread_mutex_trylock進行加鎖操作;否則將返回EBUSY錯誤碼。註意:這裏討論的pthread_mutex_lock和pthread_mutex_trylock是針對普通鎖而言的,對於其他類型的鎖,這兩個加鎖函數會有不同的行為.
pthread_mutex_unlock以原子操作方式給壹個互斥鎖進行解鎖操作。如果此時有其他線程正在等待這個互斥鎖,則這些線程中的壹個將獲得它.
三個打印機輪流打印:
輸出結果:
如果說互斥鎖是用於同步線程對***享數據的訪問的話,那麽條件變量就是用於在線程之間同步***享數據的值.條件變量提供了壹種線程之間通信的機制:當某個***享數據達到某個值時,喚醒等待這個***享數據的線程.
條件變量會在條件不滿足的情況下阻塞線程.且條件變量和互斥量壹起使用,允許線程以無競爭的方式等待特定的條件發生.
其中pthread_cond_broadcast函數以廣播的形式喚醒所有等待目標條件變量的線程,pthread_cond_signal函數用於喚醒壹個等待目標條件變量線程.但有時候我們可能需要喚醒壹個固定的線程,可以通過間接的方法實現:定義壹個能夠唯壹標識目標線程的全局變量,在喚醒等待條件變量的線程前先設置該變量為目標線程,然後采用廣播的方式喚醒所有等待的線程,這些線程被喚醒之後都檢查該變量以判斷是否是自己.
采用條件變量+互斥鎖實現生產者消費者模型:
運行結果:
阻塞隊列+生產者消費者
運行結果: