C++線程中的幾種鎖


線程之間的鎖有:互斥鎖、條件鎖、自旋鎖、讀寫鎖、遞歸鎖。一般而言,鎖的功能越強大,性能就會越低。

1、互斥鎖

互斥鎖用於控制多個線程對他們之間共享資源互斥訪問的一個信號量。也就是說是為了避免多個線程在某一時刻同時操作一個共享資源。例如線程池中的有多個空閑線程和一個任務隊列。任何是一個線程都要使用互斥鎖互斥訪問任務隊列,以避免多個線程同時訪問任務隊列以發生錯亂。

在某一時刻,只有一個線程可以獲取互斥鎖,在釋放互斥鎖之前其他線程都不能獲取該互斥鎖。如果其他線程想要獲取這個互斥鎖,那么這個線程只能以阻塞方式進行等待。

頭文件:<pthread.h>

類型:pthread_mutex_t,

函數:pthread_mutex_init(pthread_mutex_t * mutex, const phtread_mutexattr_t * mutexattr);//動態方式創建鎖,相當於new動態創建一個對象

            pthread_mutex_destory(pthread_mutex_t *mutex)//釋放互斥鎖,相當於delete

            pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//以靜態方式創建鎖

            pthread_mutex_lock(pthread_mutex_t *mutex)//以阻塞方式運行的。如果之前mutex被加鎖了,那么程序會阻塞在這里。

            pthread_mutex_unlock(pthread_mutex_t *mutex)

            int pthread_mutex_trylock(pthread_mutex_t * mutex);//會嘗試對mutex加鎖。如果mutex之前已經被鎖定,返回非0,;如果mutex沒有被鎖定,則函數返回並鎖定mutex

                                                                                                           //該函數是以非阻塞方式運行了。也就是說如果mutex之前已經被鎖定,函數會返回非0,程序繼續往下執行。

2、條件鎖

條件鎖就是所謂的條件變量,某一個線程因為某個條件為滿足時可以使用條件變量使改程序處於阻塞狀態。一旦條件滿足以“信號量”的方式喚醒一個因為該條件而被阻塞的線程。最為常見就是在線程池中,起初沒有任務時任務隊列為空,此時線程池中的線程因為“任務隊列為空”這個條件處於阻塞狀態。一旦有任務進來,就會以信號量的方式喚醒一個線程來處理這個任務。這個過程中就使用到了條件變量pthread_cond_t。

頭文件:<pthread.h>

類型:pthread_cond_t

函數:pthread_cond_init(pthread_cond_t * condtion, const phtread_condattr_t * condattr);//對條件變量進行動態初始化,相當於new創建對象

            pthread_cond_destory(pthread_cond_t * condition);//釋放動態申請的條件變量,相當於delete釋放對象

            pthread_cond_t condition = PTHREAD_COND_INITIALIZER;//靜態初始化條件變量

            pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);//該函數以阻塞方式執行。如果某個線程中的程序執行了該函數,那么這個線程就會以阻塞方式等待,直到收到pthread_cond_signal或者pthread_cond_broadcast函數發來的信號而被喚醒。

注意:pthread_cond_wait函數的語義相當於:首先解鎖互斥鎖,然后以阻塞方式等待條件變量的信號,收到信號后又會對互斥鎖加鎖。

           為了防止“虛假喚醒”,該函數一般放在while循環體中。例如

 

  1.  
    pthread_mutex_lock(mutex); //加互斥鎖
  2.  
    while(條件不成立)//當前線程中條件變量不成立
  3.  
    {
  4.  
    pthread_cond_wait(cond, mutex); //解鎖,其他線程使條件成立發送信號,加鎖。
  5.  
    }
  6.  
    ... //對進程之間的共享資源進行操作
  7.  
    pthread_mutex_unlock(mutex); //釋放互斥鎖

 

            pthread_cond_signal(pthread_cond_t * cond);//在另外一個線程中改變線程,條件滿足發送信號。喚醒一個等待的線程(可能有多個線程處於阻塞狀態),喚醒哪個線程由具體的線程調度策略決定

            pthread_cond_broadcast(pthread_cond_t * cond);//以廣播形式喚醒所有因為該條件變量而阻塞的所有線程,喚醒哪個線程由具體的線程調度策略決定

            pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * time);//以阻塞方式等待,如果時間time到了條件還沒有滿足還是會結束

3、自旋鎖

前面的兩種鎖是比較常見的鎖,也比較容易理解。下面通過比較互斥鎖和自旋鎖原理的不同,這對於真正理解自旋鎖有很大幫助。

假設我們有一個兩個處理器core1和core2計算機,現在在這台計算機上運行的程序中有兩個線程:T1和T2分別在處理器core1和core2上運行,兩個線程之間共享着一個資源。

首先我們說明互斥鎖的工作原理,互斥鎖是是一種sleep-waiting的鎖。假設線程T1獲取互斥鎖並且正在core1上運行時,此時線程T2也想要獲取互斥鎖(pthread_mutex_lock),但是由於T1正在使用互斥鎖使得T2被阻塞。當T2處於阻塞狀態時,T2被放入到等待隊列中去,處理器core2會去處理其他任務而不必一直等待(忙等)。也就是說處理器不會因為線程阻塞而空閑着,它去處理其他事務去了。

而自旋鎖就不同了,自旋鎖是一種busy-waiting的鎖。也就是說,如果T1正在使用自旋鎖,而T2也去申請這個自旋鎖,此時T2肯定得不到這個自旋鎖。與互斥鎖相反的是,此時運行T2的處理器core2會一直不斷地循環檢查鎖是否可用(自旋鎖請求),直到獲取到這個自旋鎖為止。

從“自旋鎖”的名字也可以看出來,如果一個線程想要獲取一個被使用的自旋鎖,那么它會一致占用CPU請求這個自旋鎖使得CPU不能去做其他的事情,直到獲取這個鎖為止,這就是“自旋”的含義。

當發生阻塞時,互斥鎖可以讓CPU去處理其他的任務;而自旋鎖讓CPU一直不斷循環請求獲取這個鎖。通過兩個含義的對比可以我們知道“自旋鎖”是比較耗費CPU的

頭文件:<linux\spinlock.h>

自旋鎖的類型:spinlock_t

相關函數:初始化:spin_lock_init(spinlock_t *x);

           spin_lock(x);   //只有在獲得鎖的情況下才返回,否則一直“自旋”

                 spin_is_locked(x)//  該宏用於判斷自旋鎖x是否已經被某執行單元保持(即被鎖),如果是,   返回真,否則返回假。

注意:自旋鎖適合於短時間的的輕量級的加鎖機制。

4、讀寫鎖

說到讀寫鎖我們可以借助於“讀者-寫者”問題進行理解。首先我們簡單說下“讀者-寫者”問題。

計算機中某些數據被多個進程共享,對數據庫的操作有兩種:一種是讀操作,就是從數據庫中讀取數據不會修改數據庫中內容;另一種就是寫操作,寫操作會修改數據庫中存放的數據。因此可以得到我們允許在數據庫上同時執行多個“讀”操作,但是某一時刻只能在數據庫上有一個“寫”操作來更新數據。這就是一個簡單的讀者-寫者模型。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM