Linux 互斥量、線程鎖使用總結

        在多線程編程中,引入了對象互斥鎖的概念,來保證共享數據操作的完整性。 每個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻, 線程訪問該對象的獨佔性。

一、互斥鎖的操作函數:

#include <pthread.h>



int pthread_mutex_destroy(pthread_mutex_t *mutex);

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);

對線程鎖進行操作的函數有很多,還包括許多線程鎖屬性的操作函數, 不過一般來說,對於並不複雜的情況, 只需要使用創建、獲取鎖、釋放鎖、刪除鎖這幾個就足夠了。

(1).創建互斥鎖對象

pthread_mutex_t mtx;

(2).初始化互斥鎖

//第二個參數是 NULL 的話,互斥鎖的屬性會設置爲默認屬性

pthread_mutex_init(&mtx, NULL);

//除了使用 pthread_mutex_init() 初始化一個互斥鎖,我們還可以使用下面的方式定義一個互斥鎖

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

(3).獲取互斥鎖

接下來是如何使用互斥鎖進行互斥操作。在進行互斥操作的時候, 應該先"拿到鎖"再執行需要互斥的操作,否則可能會導致多個線程都需要訪問的數據結果不一致。 例如在一個線程在試圖修改一個變量的時候,另一個線程也試圖去修改這個變量, 那就很可能讓後修改的這個線程把前面線程所做的修改覆蓋了。

下面是獲取鎖的操作:

阻塞調用:

pthread_mutex_lock(&mtx);

       這個操作是阻塞調用的,也就是說,如果這個鎖此時正在被其它線程佔用, 那麼 pthread_mutex_lock() 調用會進入到這個鎖的排隊隊列中,並會進入阻塞狀態, 直到拿到鎖之後纔會返回。

非阻塞調用:

      如果不想阻塞,而是想嘗試獲取一下,如果鎖被佔用咱就不用,如果沒被佔用那就用, 這該怎麼實現呢?可以使用 pthread_mutex_trylock() 函數。 這個函數和 pthread_mutex_lock() 用法一樣,只不過當請求的鎖正在被佔用的時候, 不會進入阻塞狀態,而是立刻返回,並返回一個錯誤代碼 EBUSY,意思是說, 有其它線程正在使用這個鎖。

int err = pthread_mutex_trylock(&mtx);

if(0 != err) {

    if(EBUSY == err) {

        //The mutex could not be acquired because it was already locked.

    }

}

(4).釋放互斥鎖

用完了互斥鎖,一定要記得釋放,不然下一個想要獲得這個鎖的線程, 就只能去等着了,如果那個線程很不幸的使用了阻塞等待,那就一直等下去了。

pthread_mutex_unlock(&mtx);

(5).銷燬線程鎖

       在使用pthread_mutex_destroy()函數銷燬一個線程鎖後,線程鎖的狀態變爲"未定義"。有的 pthread_mutex_destroy 實現方式,會使線程鎖變爲一個不可用的值。一個被銷燬的線程鎖可以被 pthread_mutex_init() 再次初始化。對被銷燬的線程鎖進行其它操作,其結果是未定義的。對一個處於已初始化但未鎖定狀態的線程鎖進行銷燬是安全的。儘量避免對一個處於鎖定狀態的線程鎖進行銷燬操作。

pthread_mutex_destroy(&mtx)

二、讀寫鎖:

      讀寫鎖與互斥量類似,但它允許更高的並行性。互斥量只有兩種狀態:鎖住和未鎖住,且一次只有一個線程可以對它加鎖。讀寫鎖可以有三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態和不加鎖狀態。一次只有一個線程可以佔有寫模式的讀寫鎖,但多個線程可以同時佔有讀模式的讀寫鎖。讀寫鎖非常適合於對數據結構讀的次數遠遠大於寫的情況。與互斥量相比,讀寫鎖在使用之前必須初始化,在釋放它們底層的內存之前必須銷燬。使用讀寫鎖相關的函數如下:

#include <pthread.h>



int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

int pthread_rwlock_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_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

條件變量:

       條件變量與互斥量一直使用時,允許線程以無競爭的方式等待特定的條件發生。條件變量是線程可用的另一種同步機制。條件本身是由互斥量保護的。線程在改變條件狀態之前必須產生鎖住互斥量,其他線程在獲得互斥量之前不會到這種改變,因爲互斥量必須在鎖定以後才能計算條件。在使用條件變量之前必須先對它初始化。釋放條件變量底層的內存空間之前,對條件變量反初始化。使用的相關函數如下:

#include <pthread.h>



int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

int pthread_cond_destroy(pthread_cond_t *cond);



int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

三、自旋鎖:

       與互斥量類似,但它不是通過休眠使進程阻塞,而是在獲取鎖之前一直處於忙等阻塞狀態。當鎖被持有的時間較短,而且線程不希望在重新調度上花費太多成本的情況下使用自旋鎖。當自旋鎖用在非搶佔式內核中時是非常有用的,除了提供互斥機制以外,它們會阻塞中斷,這樣中斷處理程序就不會讓系統陷入死鎖狀態,因爲它需要獲取已被加鎖的自旋鎖。在用戶層,自旋鎖並不非常有用。很多互斥量的實現非常高效,甚至與採用自旋鎖是同行效率的。

使用自旋鎖的相關函數如下。

#include <pthread.h>



int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

int pthread_spin_destroy(pthread_spinlock_t *lock);



int pthread_spin_lock(pthread_spinlock_t *lock);

int pthread_spin_trylock(pthread_spinlock_t *lock);

int pthread_spin_unlock(pthread_spinlock_t *lock);

 

 

 

 

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