自旋鎖(Spin Lock)
自旋鎖類似於互斥量,不過自旋鎖不是通過休眠阻塞進程,而是在取得鎖之前一直處於忙等待的阻塞狀態。這個忙等的阻塞狀態,也叫做自旋。
自旋鎖通常作為底層原語實現其他類型的鎖。
適用場景:
1)鎖被持有的時間短,而且線程不希望在重新調度上花費太多的成本;
2)在非搶占式內核中,會阻塞中斷,這樣中斷處理程序不會讓系統陷入死鎖狀態。因為中斷處理程序無法休眠,只能使用這種鎖;
缺點:
1)線程自旋等待鎖變成可用時,CPU不能做其他事情,會浪費CPU資源;
偽代碼
S = 1
線程P:
// 進入區
while (S <= 0) ; // 自旋
S--; // P操作
... // 臨界區
// 退出區
S++; // V操作
自旋鎖接口
自旋鎖接口與互斥量類似,容易相互替換。
#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);
注意:
1)如果自旋鎖當前在解鎖狀態,pthread_spin_lock不用自旋,就可以對它加鎖;
2)如果自旋鎖當前在加鎖狀態,再獲得鎖的結果是未定義的。如果調用pthread_spin_lock,會返回EDEADLK錯誤或其他錯誤,或者調用者可能會永久自旋。取決於具體實現。
3)試圖對沒有加鎖的自旋鎖解鎖,結果也是未定義的。
示例
自旋鎖使用
#include <pthread.h>
#include <stdio.h>
#define THREAD_NUM 100
pthread_spinlock_t spinlock;
void *thread_main(void *arg)
{
int id = (int)arg;
pthread_spin_lock(&spinlock); // 獲得鎖
printf("thread main %d get the lock begin\n", id);
printf("thread main %d get the lock end\n", id);
pthread_spin_unlock(&spinlock); // 釋放鎖
return NULL;
}
int main()
{
pthread_spin_init(&spinlock, 0); /* PTHREAD_PROCESS_PRIVATE == 0*/
int x = PTHREAD_PROCESS_PRIVATE;
printf("x = %d\n", x);
int i;
pthread_t tids[THREAD_NUM];
for (i = 0; i < THREAD_NUM; i++) {
pthread_create(&tids[i], NULL, thread_main, i); // 創建線程
}
for (i = 0; i < THREAD_NUM; i++) {
pthread_join(tids[i], NULL); // 連接線程
}
return 0;
}
互斥量(互斥鎖, Mutex)
互斥量(Mutex)通過休眠阻塞進程/線程,確保同一時間只有一個線程訪問數據。休眠,也就意味着會放棄CPU資源。
加鎖
對互斥量加鎖后,任何其他試圖再次對互斥量加鎖的線程,都會被阻塞,直到當前線程釋放該互斥鎖。
解鎖
如果阻塞在該互斥鎖上的線程有多個,當鎖可用時,所有線程都會變成可運行狀態,第一個變為運行的線程,就可以對互斥量加鎖,其他線程則再次等待鎖而進入休眠。
適用場景
多線程或多進程運行環境,需要對臨界區資源進行保護時。
缺點
1)使用不當,任意導致死鎖;
2)無法表示臨界區資源可用數量(由信號量解決);
接口
#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); // 函數方式初始化,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); // 解鎖
使用示例
#define THREAD_NUM 100
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_main(void *arg)
{
int id = (int)arg;
pthread_mutex_lock(&mutex);
printf("thread main %d get the lock begin\n", id);
printf("thread main %d get the lock end\n", id);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
int i;
pthread_t tids[THREAD_NUM];
for (i = 0; i < THREAD_NUM; i++) {
pthread_create(&tids[i], NULL, thread_main, i);
}
for (i = 0; i < THREAD_NUM; i++) {
pthread_join(tids[i], NULL);
}
return 0;
}
讀寫鎖(Read-Write Lock)
讀寫鎖類似於互斥量,不過讀寫鎖允許更高的並行性。讀寫鎖,也叫共享互斥鎖(shared-exclusive lock)。
當讀寫鎖以讀模式鎖住時,可以說成是以共享模式鎖住的。當以寫模式鎖住時,可以說成是以互斥模式鎖住的。
讀寫鎖與互斥鎖的區別
讀寫鎖與互斥鎖的區別在於:
互斥鎖 要么是加鎖狀態,要么是不加鎖狀態,而且一次只有一個線程能取得鎖、對其加鎖;
讀寫鎖 可以有3種狀態:讀模式加鎖,寫模式加鎖,不加鎖。一次只有一個線程能占有寫模式的讀寫鎖,不過多個線程可以同時占有讀模式的讀寫鎖。
1)當讀寫鎖是寫加鎖狀態時,在被解鎖前,所有試圖對其加鎖的線程都會被阻塞。
2)當讀寫鎖是讀加鎖狀態時,在被解鎖前,所有以讀模式加鎖的線程都可以得到訪問權,以寫模式加鎖的線程會被阻塞。
簡而言之,讀寫鎖是讀狀態與讀狀態之間共享,與寫狀態之間互斥,寫狀態是與任何狀態互斥。
互斥鎖是只有加鎖和解鎖狀態,加鎖狀態之間互斥。
適用場景
讀寫鎖非常適合對數據結構進行讀操作的次數 遠大於寫的情況。
使用接口
初始化銷毀:
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 直接賦值方式初始化讀寫鎖
讀、寫模式獲得鎖,解鎖:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 讀模式取得鎖
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 讀模式取得鎖的條件版本
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 寫模式取得鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 寫模式取得鎖的條件版本
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解鎖
注意:
1)不論是處於寫模式,還是讀模式,都可以用pthread_rwlock_unlock解鎖。
2)條件版本不會阻塞線程。
讀寫鎖屬性
attr = NULL,表示使用默認的讀寫鎖屬性:PTHREAD_PROCESS_PRIVATE,表示只在單個進程內的不同線程間共享。另外,還支持屬性PTHREAD_PROCESS_SHARED,表示讀寫鎖將在不同進程間共享。
要設置非默認屬性,就要使用下面2個函數初始化、銷毀讀寫鎖屬性。
#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destory(pthread_rwlockattr_t *attr);
獲取、設置非默認屬性:
#include <pthread.h>
int pthread_rwlockattr_getshared(pthread_rwlockattr_t *attr, int *valptr);
int pthread_rwlockattr_setshared00(pthread_rwlockattr_t *attr, int value);
要設置的當前值value,其值只能是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。