C里提供了保證線程安全性的三種方法:
(添加頭文件#include<pthread.h>,pthread 庫不是 Linux 系統默認的庫,連接時需要使用靜態庫 libpthread.a, 在編譯中要加 -lpthread參數)
- 互斥鎖
通過鎖的機制實現線程間的互斥,同一時刻只有一個線程可以鎖定它,當一個鎖被某個線程鎖定的時候,如果有另外一個線程嘗試鎖定這個臨界區(互斥體),則第二個線程會被阻塞,或者說被置於等待狀態。只有當第一個線程釋放了對臨界區的鎖定,第二個線程才能從阻塞狀態恢復運行。
int pthread_mutex_init(pthread_mutex_t* mutex, const thread_mutexattr_t* mutexattr);初始化一個互斥鎖。
int pthread_mutex_lock(pthread_mutex_t* mutex);如果mutex被鎖定,當前進程處於等待狀態;否則,本進程獲得互斥鎖並進入臨界區。
int pthread_mutex_trylock(pthread_mutex_t* mutex);和lock不同的時候,嘗試獲得互斥鎖不成功不會使得進程進入阻塞狀態,而是繼續返回線程執行。該函數可以有效避免循環等待鎖,如果trylock失敗可以釋放已經占有的資源,這樣可以避免死鎖。
int pthread_mutex_unlock(pthread_mutex_t* mutex);釋放互斥鎖,並使得被阻塞的線程獲得互斥鎖並執行。
int pthread_mutex_destroy(pthread_mutex_t* mutex);用來撤銷互斥鎖的資源。
pthread_mutex_t mutex; pthread_mutex_init(&mutex,NULL); void pthread1(void* arg){ pthread_mutex_lock(&mutex); .....//臨界區 pthread_mutex_unlock(&mutex); } void pthread2(void* arg){ pthread_mutex_lock(&mutex); .....//臨界區 pthread_mutex_unlock(&mutex); }
- 讀寫鎖
讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。適用於讀的次數大於寫的次數的數據結構。
一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可以同時占有讀模式的讀寫鎖。
讀鎖鎖住,加讀鎖,可以;加寫鎖會被阻塞,但此時會阻塞后續的讀鎖請求,防止讀鎖長期占用無法進入寫模式。寫鎖就是互斥鎖。
int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr);初始化讀寫鎖
int pthread_destroy(pthread_rwlock_t* rwlock);銷毀讀寫鎖
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);加讀鎖
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);加寫鎖
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);解鎖
- 條件變量
信號量只有鎖住和不鎖兩種狀態,而且當條件變量和信號量一起使用時,允許線程以無競爭的方式等待特定的條件發生。
條件本身是由互斥量保護的:線程在改變條件狀態之前必須先鎖住互斥量。
int pthread_cond_init(pthread_cond_t* cond,const pthread_condattr_t* attr);初始化動態分配的條件變量;也可以直接用PTHREAD_INITIALIZER直接賦值給靜態的條件變量
int pthread_cond_destroy(pthread_cond_t* cond)撤銷條件變量資源;
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);使用該函數使得等待條件變量為真。線程被條件變量cond阻塞。
int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex,const struct timespec* tspr);與wait類似,只是經歷tspr時間后,即使條件變量不滿足,阻塞也被解除,返回錯誤碼。
int pthread_cond_signal(pthread_cond_t* cond);喚醒因為條件變量阻塞的線程。
int pthread_cond_broadcast(pthread_cond_t* cond);喚醒等待該條件的所有線程。
pthread_cond_t cond; pthread_mutex_t mutex; int count=0; void pthread1(void* arg){ pthread_mutex_lock(&mutex); while(count==0) pthread_cond_wait(&cond,&mutex); count--; pthread_mutex_unlock(&mutex); } void pthread2(void* arg){ pthread_mutex_lock(&mutex); if(count==0) pthread_cond_signal(&cond); count++; pthread_mutex_unlock(&mutex); }
- 自旋鎖
互斥量阻塞線程的方式是使其進入睡眠,而自旋鎖是讓線程忙等,即不會使其睡眠,而是不斷循判斷自旋鎖已經被解鎖。
適用於占用自旋鎖時間比較短的情況。
- 信號量
介紹一下POSIX(POSIX標准定義了操作系統應該為應用程序提供的接口標准,換句話說,為一個POSIX兼容的操作系統編寫的程序,應該可以在任何其它的POSIX操作系統(即使是來自另一個廠商)上編譯執行。)的信號量機制,定義在頭文件/usr/include/semaphore.h
1)初始化一個信號量:sem_init()
int sem_init(sem_t* sem,int pshared,unsigned int value);
pshared為0時表示該信號量只能在當前進程的線程間共享,否則可以進程間共享,value給出了信號量的初始值。
2)阻塞線程
sem_wait(sem_t* sem)直到信號量sem的值大於0,解除阻塞后將sem的值減一,表明公共資源經使用后減少;sem_trywait(sem_t* sem)是wait的非阻塞版本,它直接將sem的值減一,相當於P操作。
3)增加信號量的值,喚醒線程
sem_post(sem_t* sem)會使已經被阻塞的線程其中的一個線程不再阻塞,選擇機制同樣是由線程的調度策略決定的。相當於V操作。
3)釋放信號量資源
sem_destroy(sem_t* sem)用來釋放信號量sem所占有的資源
pthread_mutex_t mutex; sem_t full,empty; void producer(void* arg){ while(1){ sem_wait(&empty);//need to produce. the the empty of resource need minus 1 pthread_mutex_lock(&mutex); ...//produce a resource pthread_mutex_unlock(&mutex); sem_post(&full); //have produced a resource, the the full of resource need add 1 } } void consumer(void* arg){ while(1){ sem_wait(&full); pthread_mutex_lock(&mutex); ...//consume a resource pthread_mutex_unlock(&mutex); sem_post(&empty); } }