自旋鎖概述
自旋鎖是一種多線程同步機制,旨在保護共享資源免受并發(fā)訪問的影響。在多個線程嘗試獲取鎖時,它們會持續(xù)在循環(huán)中自旋(即不斷檢查鎖是否可用),而不是立即進入休眠狀態(tài)等待鎖的釋放。這種方法減少了線程切換的開銷,適合于短時間內(nèi)鎖的競爭情況。然而,不恰當(dāng)?shù)氖褂每赡軙?dǎo)致cpu資源的浪費。
自旋鎖的原理
自旋鎖通常使用一個共享的標(biāo)志位(例如一個布爾值)來表示鎖的狀態(tài)。當(dāng)標(biāo)志位為true時,表示鎖已被某個線程占用;當(dāng)標(biāo)志位為false時,表示鎖可用。當(dāng)一個線程嘗試獲取自旋鎖時,它會不斷檢查標(biāo)志位:
- 如果標(biāo)志位為false,表示鎖可用,線程將設(shè)置標(biāo)志位為true,表示自己占用了鎖,并進入臨界區(qū)。
- 如果標(biāo)志位為true(即鎖已被其他線程占用),線程會在一個循環(huán)中持續(xù)自旋等待,直到鎖被釋放。
自旋鎖的優(yōu)點與缺點
優(yōu)點:
- 低延遲:自旋鎖適用于短時間內(nèi)的鎖競爭情況,因為它不會讓線程進入休眠狀態(tài),從而避免了線程切換的開銷,提高了鎖操作的效率。
- 減少系統(tǒng)調(diào)度開銷:等待鎖的線程不會被阻塞,不需要上下文切換,從而減少了系統(tǒng)調(diào)度的開銷。
缺點:
- CPU資源浪費:如果鎖的持有時間較長,等待獲取鎖的線程會一直循環(huán)等待,導(dǎo)致CPU資源的浪費。
- 可能引起活鎖:當(dāng)多個線程同時等待一個鎖時,如果沒有適當(dāng)?shù)耐吮懿呗裕赡軙?dǎo)致所有線程都在不斷檢查鎖狀態(tài)而無法進入臨界區(qū),形成活鎖。
自旋鎖的使用場景
- 短暫等待的情況:適用于鎖被占用時間非常短的場景,如多線程對共享數(shù)據(jù)進行簡單的讀寫操作。
- 多線程鎖使用:通常用于系統(tǒng)底層,同步多個CPU對共享資源的訪問。
linux提供的自旋鎖系統(tǒng)調(diào)用
int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); int pthread_spin_unlock(pthread_spinlock_t *lock); int pthread_spin_init(pthread_spinlock_t *lock, int pshared); int pthread_spin_destroy(pthread_spinlock_t *lock);
- pshared有兩個選項,PTHREAD_PROCESS_private和PTHREAD_PROCESS_SHARED。private選項表示自旋鎖只能在同一進程內(nèi)的多個線程內(nèi)使用,pshared表示可以在多個不同的進程內(nèi)使用同一個自旋鎖。
自旋鎖的注意事項
- 在使用自旋鎖時,需要確保鎖被釋放的時間盡可能短,以避免CPU資源的浪費。
- 在多CPU環(huán)境下,自旋鎖可能不如其他鎖機制高效,因為它可能導(dǎo)致線程在不同的CPU上自旋等待。
自旋鎖的結(jié)論
自旋鎖是一種適用于短時間內(nèi)鎖競爭情況的同步機制,它通過減少線程切換的開銷來提高鎖操作的效率。然而,它也存在CPU資源浪費和可能引起活鎖等缺點。在使用自旋鎖時,需要根據(jù)具體的應(yīng)用場景進行選擇,并確保鎖被釋放的時間盡可能短。
樣例代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> int ticket = 1000; pthread_spinlock_t lock; void *route(void *arg) { char *id = (char *)arg; while (1) { pthread_spin_lock(&lock); if (ticket > 0) { usleep(1000); printf("%s sells ticket:%dn", id, ticket); ticket--; pthread_spin_unlock(&lock); } else { pthread_spin_unlock(&lock); break; } } return NULL; } int main(void) { pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE); pthread_t t1, t2, t3, t4; pthread_create(&t1, NULL, route, (void *)"thread 1"); pthread_create(&t2, NULL, route, (void *)"thread 2"); pthread_create(&t3, NULL, route, (void *)"thread 3"); pthread_create(&t4, NULL, route, (void *)"thread 4"); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); pthread_spin_destroy(&lock); return 0; }
讀寫鎖概述
在編寫多線程程序時,常見一種情況是公共數(shù)據(jù)的修改機會較少,而讀取的機會則相對較多。通常,讀取過程伴隨著查找操作,耗時較長。如果對這種代碼段加鎖,會極大地降低程序的效率。針對這種多讀少寫的情況,有一種專門的處理方法,即讀寫鎖。
pthread庫為我們提供了讀寫鎖。
讀寫鎖的初始化和銷毀
讀寫鎖的加鎖和解鎖
樣例代碼
#include <iostream> #include <pthread.h> #include <unistd.h> #include <vector> #include <cstdlib> #include <ctime> // 共享資源 int shared_data = 0; // 讀寫鎖 pthread_rwlock_t rwlock; // 讀者線程函數(shù) void *Reader(void *arg) { int number = *(int *)arg; while (true) { pthread_rwlock_rdlock(&rwlock); // 讀者加鎖 std::cout << "Reader " << number << " is reading. Shared data: " << shared_data << std::endl; pthread_rwlock_unlock(&rwlock); // 讀者解鎖 sleep(1); // 模擬讀取時間 } return NULL; } // 寫者線程函數(shù) void *Writer(void *arg) { int number = *(int *)arg; while (true) { pthread_rwlock_wrlock(&rwlock); // 寫者加鎖 shared_data++; std::cout << "Writer " << number << " is writing. Shared data: " << shared_data << std::endl; pthread_rwlock_unlock(&rwlock); // 寫者解鎖 sleep(1); // 模擬寫入時間 } return NULL; } int main() { pthread_rwlock_init(&rwlock, NULL); std::vector<pthread_t> threads; int num_readers = 5; int num_writers = 2; for (int i = 0; i < num_readers; i++) { int *arg = new int(i); pthread_t thread; pthread_create(&thread, NULL, Reader, arg); threads.push_back(thread); } for (int i = 0; i < num_writers; i++) { int *arg = new int(i); pthread_t thread; pthread_create(&thread, NULL, Writer, arg); threads.push_back(thread); } for (auto thread : threads) { pthread_join(thread, NULL); } pthread_rwlock_destroy(&rwlock); return 0; }
讀者優(yōu)先策略
在這種策略中,系統(tǒng)會盡可能多地允許多個讀者同時訪問資源(比如共享文件或數(shù)據(jù)),而不會優(yōu)先考慮寫者。這意味著當(dāng)有讀者正在讀取時,新到達的讀者會立即被允許進入讀取區(qū),而寫者則會被阻塞,直到所有讀者都離開讀取區(qū)。讀者優(yōu)先策略可能會導(dǎo)致寫者饑餓(即寫者長時間無法獲得寫入權(quán)限),特別是當(dāng)讀者頻繁到達時。
寫者優(yōu)先策略
在這種策略中,系統(tǒng)會優(yōu)先考慮寫者。當(dāng)寫者請求寫入權(quán)限時,系統(tǒng)會盡快地讓寫者進入寫入?yún)^(qū),即使此時有讀者正在讀取。這通常意味著一旦有寫者到達,所有后續(xù)的讀者都會被阻塞,直到寫者完成寫入并離開寫入?yún)^(qū)。寫者優(yōu)先策略可以減少寫者等待的時間,但可能會導(dǎo)致讀者饑餓(即讀者長時間無法獲得讀取權(quán)限),特別是當(dāng)寫者頻繁到達時。