🐶博主主頁:@??. 一懷明月??
???🔥專欄系列:線性代數,C初學者入門訓練,題解C,C的使用文章,「初學」C++,linux
🔥座右銘:“不要等到什么都沒有了,才下定決心去做”
🚀🚀🚀大家覺不錯的話,就懇求大家點點關注,點點小愛心,指點指點🚀🚀🚀
目錄
互斥鎖
scp指令
線程加鎖的本質
可重入VS線程安全(多執行流并發執行同一段代碼時造成的數據不一致)
死鎖
線程同步
生產者消費者模型
條件變量
pthread_cond_wait
pthread_cond_signal
pthread_cond_broadcast
事例:火車票搶購
互斥鎖
創建全局的鎖
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//創建全局的鎖,用宏去初始化
創建局部的鎖
pthread_mutex_t mutex;//創建局部鎖的時候不用宏初始化 pthread_mutex_init(&mutex);//初始化 pthread_mutex_destroy(&mutex);//局部的鎖,不用時要銷毀
scp指令
scp指令是用于在Linux系統中進行文件傳輸的命令,
其語法如下:
scp [選項] [源文件] [目標文件] 常用選項包括:* -r:遞歸復制整個目錄 * -P:指定端口號 * -p:保持源文件的修改時間、訪問時間和權限信息 * -q:安靜模式,不顯示傳輸進度信息 示例用法:1. 從本地復制文件到遠程服務器: scp /path/to/local/file username@remotehost:/path/to/remote/directory2. 從遠程服務器復制文件到本地: scp username@remotehost:/path/to/remote/file /path/to/local/directory3. 從遠程服務器復制整個目錄到本地: scp -r username@remotehost:/path/to/remote/directory /path/to/local/directory
線程加鎖的本質
關于加鎖的原則,一般原則:誰加鎖,誰解鎖
可重入VS線程安全(多執行流并發執行同一段代碼時造成的數據不一致)
可重入函數是線程安全函數的一種
線程安全不一定是可重入的,而可重入函數則一定是線程安全的。
如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。
死鎖
原因
1)當兩個線程需要都需要兩把鎖,而它們每一個線程只有一把鎖,此時就會導致死鎖
2)當一個線程在加鎖的時候,沒有解鎖,又去加鎖,這樣就會導致死鎖
…….
死鎖必要條件(產生死鎖的時候,這四個條件一定是滿足的)
互斥條件:一個資源每次只能被一個執行流使用
請求與保持條件:一個執行流因請求資源而阻塞時,對已獲得的資源保持不放
不剝奪條件:一個執行流已獲得的資源,在末使用完之前,不能強行剝奪
循環等待條件:若干執行流之間形成一種頭尾相接的循環等待資源的關系
避免死鎖
破壞死鎖的四個必要條件
加鎖順序一致
避免鎖未釋放的場景
資源一次性分配
線程同步
線程同步的概念:
在臨界資源使用安全的前提下,讓多線程執行具有一定的順序性——同步
互斥:能夠保證資源的安全性
同步:能夠較為充分使用我們的資源
生產者消費者模型
3種關系
生產者之間關系,互斥
消費者之間關系,互斥
生產者與消費者之間關系,互斥和同步
本質:就是用鎖和條件變量,維護
2種角色
生產者,消費者——線程或者進程
1個交易場所
內存空間
生產者消費者模型提高了數據處理的能力
條件變量
創建條件變量
pthread_cond_t cond;
全局的條件變量初始化
cond=PTHREAD_COND_INITIALIZER
局部條件變量的初始化
Pthread_cond_init(&cond);
局部條件變量需要銷毀
pthread_cond_destory(&cond);
pthread_cond_wait
pthread_cond_wait() 是 POSIX 線程庫中的一個函數,用于線程間的條件變量同步。它需要與互斥鎖(mutex)和條件變量(condition variable)一起使用。
函數原型如下:int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);pthread_cond_wait() 的作用是讓調用線程等待條件變量的滿足。在調用該函數之前, 必須先使用 pthread_mutex_lock() 鎖住一個互斥鎖,以確保線程安全。 當條件變量滿足時,pthread_cond_wait() 函數會解鎖互斥鎖并使線程進入等待狀態, 直到被其他線程通過 pthread_cond_signal() 或 pthread_cond_broadcast() 喚醒。
使用示例:
#include <pthread.h> // 全局互斥鎖和條件變量 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void* thread_func(void* arg) {// 獲取互斥鎖pthread_mutex_lock(&mutex);// 等待條件變量滿足pthread_cond_wait(&cond, &mutex);// 條件變量滿足后的處理// 解鎖互斥鎖pthread_mutex_unlock(&mutex);return NULL; } int main() {pthread_t thread;// 創建線程pthread_create(&thread, NULL, thread_func, NULL);// 在某個時刻滿足條件,并通知等待的線程pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);// 等待線程結束pthread_join(thread, NULL);return 0; }
在上面的示例中,主線程通過 pthread_cond_signal() 喚醒等待的線程。當滿足條件時,等待的線程會被喚醒并繼續執行。注意,在調用 pthread_cond_wait() 之前必須加鎖(pthread_mutex_lock()),并在條件滿足后解鎖(pthread_mutex_unlock())。
pthread_cond_signal
pthread_cond_signal() 是 POSIX 線程庫中的一個函數,用于喚醒等待在條件變量上的一個線程。它會選擇一個等待的線程,并通知該線程條件已經滿足,使得該線程從等待狀態恢復到運行狀態。
函數原型如下:
int pthread_cond_signal(pthread_cond_t* cond); pthread_cond_signal() 的作用是發送一個信號給等待在條件變量上的某個線程, 使其從等待狀態返回。如果有多個線程等待在條件變量上,那么只有其中一個線程會被喚醒, 具體哪個線程被喚醒則不確定。
pthread_cond_broadcast
pthread_cond_broadcast() 是 POSIX 線程庫中的一個函數,用于廣播喚醒所有等待在條件變量上的線程。當條件滿足時,可以使用 pthread_cond_broadcast() 函數來喚醒所有等待在該條件變量上的線程,讓它們從等待狀態返回到運行狀態。
?函數原型如下:
int pthread_cond_broadcast(pthread_cond_t* cond); pthread_cond_broadcast() 的作用是向所有等待在條件變量上的線程發送信號, 使它們全部被喚醒。所有被喚醒的線程都會嘗試重新獲取互斥鎖,并繼續執行。
1.讓線程在進行等待的時候,會自定釋放鎖
2.線程被喚醒的時候,是在臨界區內喚醒的,當線程被喚醒的時候,線pthread_cond_wait返回的時候要重新申請并持有鎖
3.當線程被喚醒的時候,重新申請并持有鎖的本質是也要參與鎖的競爭
單純的互斥,能保證數據的安全,不一定合理高效!
事例:火車票搶購
test_cond
#include<iostream> #include<pthread.h> #include<unistd.h> using namespace std;pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//條件變量 pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//鎖int tickets=1000;void* threadroutine(void* args) {string name=static_cast<const char*>(args);while(true){// sleep(1);pthread_mutex_lock(&mutex);//加鎖if(tickets>0){cout<<name<<", get a ticket: "<<tickets--<<endl;usleep(100);}else{cout<<"沒票了"<<name<<endl;pthread_cond_wait(&cond,&mutex);//讓線程滿足條件變量,才被喚醒//1.讓線程在進行等待的時候,會自動釋放鎖//2.線程被喚醒的時候,是在臨界區內喚醒的,當線程被喚醒的時候,線程pthread_cond_wait返回的時候//要重新申請并持有鎖//3.當線程被喚醒的時候,重新申請并持有鎖的本質是也要參與鎖的競爭}pthread_mutex_unlock(&mutex);} }//主線程 int main() {pthread_t t1,t2,t3;pthread_create(&t1,nullptr,threadroutine,(void*)"thread - 1");pthread_create(&t2,nullptr,threadroutine,(void*)"thread - 2");pthread_create(&t3,nullptr,threadroutine,(void*)"thread - 3");sleep(5);//主線程5s之后,開始喚醒一個線程while(true){// sleep(1);// pthread_cond_signal(&cond);sleep(20);//休息20s再放一批票pthread_mutex_lock(&mutex);tickets+=1000;pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond);//讓一個線程被喚醒,才后這個線程在去搶票}pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);return 0; }
火車票搶購,其中會同時出現多個人(線程)去搶購票,所有的搶票的過程一定要是原子的,所以需要加互斥鎖。還有一個問題,在上面的搶票過程中,會在一段時間增加票的數量,但總不能讓某一個人(線程)把這些票都搶光吧!所以就需要條件變量,當這次票完的時候,將這個線程阻塞住,只有被喚醒才可以繼續參與搶票。
?🌸🌸🌸如果大家還有不懂或者建議都可以發在評論區,我們共同探討,共同學習,共同進步。謝謝大家! 🌸🌸🌸??