執行緒安全
- 1. 執行緒安全
- 執行緒安全的實作
- 2. 互斥的實作
- 3. 死鎖
- 4. 同步的實作
1. 執行緒安全
- 執行緒中對臨界資源的訪問操作是安全的
- 臨界資源:公共資源,大家都能訪問到的資源
執行緒安全的實作
同步與互斥:
- 互斥:通過同一時間只有一個執行緒能夠訪問資源實作資源訪問的安全性
- 同步:通過條件判斷讓執行緒對臨界資源訪問更加合理有序
2. 互斥的實作
- 互斥的實作:互斥鎖(通過互斥鎖保護執行緒對臨界資源的訪問操作不會被打斷)
- 本質:是一個只有0/1的計數器
- 原理:標記臨界資源的兩種訪問狀態,可訪問或者不可訪問,在執行緒訪問臨界資源之前,先進行互斥鎖加鎖操作,(判斷是都可訪問,可訪問則回傳,不可訪問則阻塞),在執行緒訪問臨界資源完畢后,進行解鎖操作
- 互斥鎖本身計數器的操作是原子操作
操作流程以及介面認識
- 定義互斥鎖變數:
pthread_mutex_t mutex - 初始化互斥鎖:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutextattr_t *attr) - 在訪問臨界資源之前進行加鎖
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_destroy(pthread_mutex_t *mutex);
黃牛搶票的例子:火車票有固定數量(計數器),四個黃牛進行搶票
int tickets=100; thread–黃牛(票數大于0,則搶票,沒有票就退出)


3. 死鎖
- 描述的是程式因為流程無法繼續推進卡死的情況
死鎖產生的原因:死鎖產生的四個必要條件:
- 互斥條件-- 同一時間只有一個執行緒能夠訪問資源
- 不可剝奪條件 --我的鎖只能我解
- 請求與保持條件 --得到A鎖然后請求B鎖,請求不到B,不釋放A
- 環路等待條件 --A執行緒拿到1鎖,然后請求2鎖,B拿到2請求1
預防死鎖:預防四個必要條件的產生-- 破壞必要條件
- 保證加/解鎖順序一致
- 使用非阻塞加鎖
避免死鎖:銀行家演算法,死鎖檢測演算法…
- 銀行家演算法:所有資源表,已經分配資源表,當前請求資源表
4. 同步的實作
- 同步的實作:通過條件判斷資源獲取是否合理 --條件變數
- 條件變數:一個pcb等待佇列+使執行緒阻塞以及喚醒的介面
- 原理:當執行緒獲取資源不合理的時候,呼叫阻塞介面,使執行緒阻塞,將pcb掛到等待佇列,等待資源訪問合理的時候,通過喚醒介面喚醒阻塞的等待佇列上的執行緒
- 注意:條件變數本身只提供阻塞與喚醒功能,而資源獲取是否合理需要程式員自己判斷完成
操作流程以及介面認識
- 定義條件變數:
pthread_cond_t cond; - 初始化條件變數:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr); - 在獲取資源不合理時呼叫阻塞介面使執行緒阻塞
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);: 解鎖,休眠,被喚醒后加鎖
int pthread_cond_timedwait(pathread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *abstime) - 其他執行緒促使資源獲取合理后,喚醒等待佇列上的執行緒
int pthread_cond_signal(pthread_cond_t *cond);–至少喚醒一個
int pthread_cond_broadcast(pthread_cond_t *cond);–喚醒所有 - 銷毀:
int pthread_cond_destroy(pthread_cond_t *cond);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
//定義條件變數
pthread_cond_t cond;
//定義互斥變數
pthread_mutex_t mutex;
void *thr_entry(void* arg)
{
while(1)
{
//阻塞
pthread_cond_wait(&cond,&mutex);
printf("I am a thread\n");
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid;
//初始化互斥鎖
pthread_mutex_init(&mutex, NULL);
//初始化條件變數
pthread_cond_init(&cond, NULL);
//創建
int ret = pthread_create(&tid, NULL,thr_entry,NULL);
if(ret != 0)
{
printf("thread create failed\n");
return -1;
}
while(1)
{
//喚醒
pthread_cond_signal(&cond);
sleep(1);
}
//銷毀
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return0;
}

廚師顧客:
- 顧客:加鎖–>判斷柜臺上有沒有飯,沒有則阻塞(等有飯了吃) -->吃飯 -->(柜臺沒有飯)喚醒廚師做飯–>解鎖
- 廚師:加鎖–>判斷柜臺上有沒有飯,有則阻塞(等沒飯了做)–>做飯–>(柜臺有飯)喚醒顧客吃飯–>解鎖
注意事項:
- 條件的判斷應該使用while回圈判斷
(三個顧客阻塞,有一碗飯,但是都被喚醒,開始加鎖,只有一個顧客加鎖成功,其他顧客卡在鎖這里,等到這個顧客吃完飯解鎖了,有可能獲取到鎖的不是廚師,而是卡在鎖這里的顧客,這時候如果沒有二次判斷有沒有飯,則肯就會出現在沒有飯的情況下吃到飯) - 在具有多種角色的情況下,應該使用多個條件變數
(三個顧客,因為沒有飯,掛在阻塞佇列上,一個廚師做好飯,喚醒一個顧客,顧客吃完喚醒等待佇列上的pcb,但是因為佇列上顧客在前,有可能喚醒的不是廚師,而是顧客,這個顧客二次判斷因為沒有飯再次陷入阻塞)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
//定義條件變數
pthread_cond_t cond_customer;
pthread_cond_t cond_cooker;
//定義互斥鎖變數
pthread_mutex_t mutex;
int counter = 0;//0-沒有飯; 1-有飯
void *cooker(void *arg)
{
while(1) {
//加鎖
pthread_mutex_lock(&mutex);
while(counter == 1) {
//阻塞
pthread_cond_wait(&cond_cooker, &mutex);
}
//做飯
printf("廚師做了一碗飯\n");
counter++;
//喚醒顧客吃飯
pthread_cond_signal(&cond_customer);
//解鎖
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *customer(void *arg)
{
while(1) {
//加鎖
pthread_mutex_lock(&mutex);
while(counter == 0) {
//阻塞
pthread_cond_wait(&cond_customer, &mutex);
}
//吃飯
printf("真好吃\n");
counter--;
//喚醒廚師做法
pthread_cond_signal(&cond_cooker);
//解鎖
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t tid1, tid2;
int ret;
//初始化條件變數
pthread_cond_init(&cond_customer, NULL);
pthread_cond_init(&cond_cooker, NULL);
//初始化互斥鎖
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < 3; i++) {
ret = pthread_create(&tid1, NULL, cooker, NULL);
if (ret != 0) {
printf("create thread error\n");
return -1;
}
}
for (int i = 0; i < 3; i++) {
ret = pthread_create(&tid2, NULL, customer, NULL);
if (ret != 0) {
printf("create thread error\n");
return -1;
}
}
//等待
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
//銷毀
pthread_cond_destroy(&cond_cooker);
pthread_cond_destroy(&cond_customer);
pthread_mutex_destroy(&mutex);
return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277795.html
標籤:其他
上一篇:Stack2 攻防世界題目分析
