?線程互斥? 庫函數strncpy?進程線程間的互斥相關背景概念臨界資源:多線程執?流共享的資源就叫做臨界資源臨界區:每個線程內部,訪問臨界資源的代碼,就叫做臨界區互斥:任何時刻,互斥保證有且只有?個執?流進?臨界區,訪問臨界資源,通常對臨界資源起保護作?原?性(后?討論如何實現):不會被任何調度機制打斷的操作,該操作只有兩態,要么完成,要么未完成?互斥量mutex?部分情況,線程使?的數據都是局部變量,變量的地址空間在線程棧空間內,這種情況,變量歸屬單個線程,其他線程?法獲得這種變量。但有時候,很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數據的共享,完成線程之間的交互。多個線程并發的操作共享變量,會帶來?些問題。
makefile文件
代碼語言:JavaScript代碼運行次數:0運行復制
bin=ticketcc=g++src=$(wildcard *.cc)obj=$(src:.cc=.o)$(bin):$(obj)$(cc) -o $@ $^ -lpthread%.o:%.cc@echo "Comiling $<p>代碼:</p>代碼語言:javascript<i class="icon-code"></i>代碼運行次數:<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>運行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>復制<pre class="prism-token token line-numbers javascript">#include <stdio.h>#include <string>#include <string.h>#include <pthread.h>#include <unistd.h>int ticket = 100;void* routine(void* args){ char *id = (char*)args; // std::string id = static_cast<const char>(args); while(1) { if(ticket > 0) { usleep(10000); printf("%s sells ticket:%dn", id, ticket); ticket--; } else { break; } } return NULLptr;}int main(void){ pthread_t t1, t2 , t3, t4; pthread_create(&t1, nullptr, routine, (void*)"thread 1"); pthread_create(&t2, nullptr, routine, (void*)"thread 2"); pthread_create(&t3, nullptr, routine, (void*)"thread 3"); pthread_create(&t4, nullptr, routine, (void*)"thread 4"); pthread_join(t1, nullptr); pthread_join(t2, nullptr); pthread_join(t3, nullptr); pthread_join(t4, nullptr); return 0;}</const></unistd.h></pthread.h></string.h></string></stdio.h>




if語句判斷條件為真以后,代碼可以并發的切換到其他進程usleep這個模擬夜漫長業務的過程這個漫長的業務過程中,可能有多個線程會進入該代碼段–ticket操作本身就不是一個原子操作
取出ticket–部分的匯編代碼
代碼語言:javascript代碼運行次數:0運行復制
objdump -d a.out > test.objdump 152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax 600b34 <ticket> 153 400651: 83 e8 01 sub $0x1,%eax154 400654: 89 05 da 04 20 00 mov%eax,0x2004da(%rip) 600b34 <ticket></ticket></ticket>
–操作并不是原子操作而是對應三條匯編指令:
load將共享變量體的從內存加載到寄存器update更新寄存器里面的只執行復議操作store:將新值從寄存器寫回共享變量ticket的內存地址
要解決以上問題需要做到三點:
代碼必須要有互斥行為:當代碼進入臨界區執行時,不允許其他進程進入該臨界區如果多個線程同時要求進入臨界區的代碼,并且臨界區沒有線程在執行,那么只能一個線程進入該臨界區如果現場不在臨界區中執行,那么該現場就不能阻止其他進程進入臨界區
要做到這三點,本身是上就是需要一把鎖,linux上提供這把鎖叫互斥量

互斥量的接口 初始化互斥量的兩種方法
方法1:靜態分配代碼語言:javascript代碼運行次數:0運行復制
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
?法2,動態分配: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 參數: mutex:要初始化的互斥量 attr:這是一個指向 pthread_mutexattr_t 類型對象的指針,該類型用于定義互斥鎖的屬性。如果將其設置為 NULL
銷毀互斥量 銷毀互斥量需要注意:
使用PTHREAD_ MUTEX_ INITIALIZER初始化的互斥量不需要銷毀不要銷毀?個已經加鎖的互斥量已經銷毀的互斥量,要確保后?不會有線程再嘗試加鎖代碼語言:javascript代碼運行次數:0運行復制
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量加鎖和解鎖
代碼語言:javascript代碼運行次數:0運行復制
int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);返回值:成功返回o,失敗返回錯誤號
調用pthread_ lock時,可能會遇到以下情況:
互斥量處于未鎖狀態,該函數會將互斥量鎖定,同時返回成功發起函數調用時,其他線程已經鎖定互斥量,或者存在其他線程同時申請互斥量,但沒有競爭到互斥量,那么pthread_lock調用會陷入阻塞(執行流被掛起),等待互斥量解鎖。
改進上面的售票系統:
代碼語言:javascript代碼運行次數:0運行復制
#include <stdio.h>#include <string>#include <string.h>#include <pthread.h>#include <unistd.h>int ticket = 100;pthread_mutex_t mutex;void* routine(void* args){ char *id = (char*)args; // std::string id = static_cast<const char>(args); while(1) { pthread_mutex_lock(&mutex); if(ticket > 0) { usleep(1000); printf("%s sells ticket:%dn", id, ticket); ticket--; pthread_mutex_unlock(&mutex); } else { pthread_mutex_unlock(&mutex); break; } } return nullptr;}int main(void){ pthread_t t1, t2 , t3, t4; pthread_create(&t1, nullptr, routine, (void*)"thread 1"); pthread_create(&t2, nullptr, routine, (void*)"thread 2"); pthread_create(&t3, nullptr, routine, (void*)"thread 3"); pthread_create(&t4, nullptr, routine, (void*)"thread 4"); pthread_join(t1, nullptr); pthread_join(t2, nullptr); pthread_join(t3, nullptr); pthread_join(t4, nullptr); pthread_mutex_destroy(&mutex); return 0;}</const></unistd.h></pthread.h></string.h></string></stdio.h>


?線程同步?條件變量當?個線程互斥地訪問某個變量時,它可能發現在其它線程改變狀態之前,它什么也做不了。例如?個線程訪問隊列時,發現隊列為空,它只能等待,只到其它線程將?個節點添加到隊列中。這種情況就需要?到條件變量。?同步概念與競態條件同步:在保證數據安全的前提下,讓線程能夠按照某種特定的順序訪問臨界資源,從?有效避免饑餓問題,叫做同步競態條件:因為時序問題,?導致程序異常,我們稱之為競態條件。在線程場景下,這種問題也 不難理解? 條件變量函數
初始化
代碼語言:javascript代碼運行次數:0運行復制
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);
參數: cond:要初始化的條件變量 attr: NULL
銷毀:
代碼語言:javascript代碼運行次數:0運行復制
int pthread_cond_destroy(pthread_cond_t *cond)
等待條件滿?
代碼語言:javascript代碼運行次數:0運行復制
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);參數:cond:要在這個條件變量上等待mutex:互斥量,后面詳細解釋
喚醒等待
代碼語言:javascript代碼運行次數:0運行復制
int pthread_cond_broadcast(pthread_cond_t *cond);int pthread_cond_signal(pthread_cond_t *cond);
簡單案例:
我們先使用PTHREAD_COND/MUTEX_INITIALIZER進行測試,對其他細節暫不追究然后將接口更改成為使用pthread_cond_init/pthread_cond_destroy的方式,方便后續進行封裝代碼語言:javascript代碼運行次數:0運行復制
#include <iostream>#include <string.h>#include <unistd.h>#include <pthread.h>pthread_cond_t cond = PTHREAD_COND_INITIALIZER;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void* active(void* args){ std::string name = static_cast<const char>(args); while(true) { pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); std::cout<figure class=""><img src="https://img.php.cn/upload/article/001/503/042/174487358175324.jpg" alt="【Linux學習指南】線程同步與互斥"></figure></const></pthread.h></unistd.h></string.h></iostream>