前端開發(fā)工具vscode網(wǎng)站關(guān)鍵詞優(yōu)化公司
目錄
復(fù)習(xí)目標(biāo):
1 互斥鎖
1.1互斥鎖的使用步驟
1.2 練習(xí)
1.3 死鎖
2 讀寫鎖
3 條件變量
4 信號量
復(fù)習(xí)目標(biāo):
- 熟練掌握互斥量的使用
- 說出什么叫死鎖以及解決方案
- 熟練掌握讀寫鎖的使用
- 熟練掌握條件變量的使用
- 理解條件變量實(shí)現(xiàn)的生產(chǎn)消費(fèi)者模型
- 理解信號量實(shí)現(xiàn)的生產(chǎn)消費(fèi)者模型
1 互斥鎖
1.1互斥鎖的使用步驟
- 第1步:創(chuàng)建一把互斥鎖
- pthread_mutex_t mutex;
- 初始化互斥鎖
- pthread_mutex_init(&mutex);---相當(dāng)于mutex=1
- 在代碼中尋找共享資源(也稱為臨界區(qū))
pthread_mutex_lock(&mutex); ?-- mutex = 0
[臨界區(qū)代碼]
pthread_mutex_unlock(&mutex); -- mutex = 1
- 釋放互斥鎖資源
pthread_mutex_destroy(&mutex);
注意:必須在所有操作共享資源的線程上都加上鎖否則不能起到同步的效果
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/types.h>//定義一把鎖
pthread_mutex_t mutex;void *mythread1(void *args)
{while (1){//加鎖pthread_mutex_lock(&mutex);printf("hello ");sleep(rand() % 3);printf("world\n");//解鎖pthread_mutex_unlock(&mutex);sleep(rand() % 3);}pthread_exit(NULL);
}void *mythread2(void *args)
{while (1){//解鎖pthread_mutex_lock(&mutex);printf("HELLO");sleep(rand() % 3);printf("WORLD\n");//解鎖pthread_mutex_unlock(&mutex);sleep(rand() % 3);}pthread_exit(NULL);
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//隨機(jī)數(shù)種子srand(time(NULL));//互斥鎖初始化pthread_mutex_init(&mutex, NULL);ret = pthread_create(&thread1, NULL, mythread1, NULL);if (ret != 0){printf("pthread_create error ,[%s]\n", strerror(ret));return -1;}ret = pthread_create(&thread2, NULL, mythread2, NULL);if (ret != 0){printf("pthread_create error ,[%s]\n", strerror(ret));return -1;}//等待線程結(jié)束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//釋放互斥鎖pthread_mutex_destroy(&mutex);system("pause");return 0;
}
?
1.2 練習(xí)
- 編寫思路:
1 定義一把互斥鎖,應(yīng)該為一全局變量
pthread_mutex_t mutex;
2 在main函數(shù)中對mutex進(jìn)行初始化
pthread_mutex_init(&mutex, NULL);
3 創(chuàng)建兩個(gè)線程,在兩個(gè)線程中加鎖和解鎖
4 主線程釋放互斥鎖資源
pthread_mutex_destroy(&mutex);
1.3 死鎖
死鎖并不是linux提供給用戶的一種使用方法,而是由于用戶使用互斥鎖不當(dāng)引起的一種現(xiàn)象。
- 常見的死鎖有兩種:
- 第一種:自己鎖自己,如下圖代碼片段
?第二種 線程A擁有A鎖,請求獲得B鎖;線程B擁有B鎖,請求獲得A鎖,這樣造成線程A和線程B都不釋放自己的鎖,而且還想得到對方的鎖,從而產(chǎn)生死鎖,如下圖所示:
- 如何解決死鎖:
- 讓線程按照一定的順序去訪問共享資源
- 在訪問其他鎖的時(shí)候,需要先將自己的鎖解開
- 調(diào)用pthread_mutex_trylock,如果加鎖不成功會立刻返回
2 讀寫鎖
- 什么是讀寫鎖
- 讀寫鎖也叫共享-獨(dú)占鎖。當(dāng)讀寫鎖以讀模式鎖住時(shí),它是以共享模式鎖住的;當(dāng)它以寫模式鎖住時(shí),它是以獨(dú)占模式鎖住的。寫?yīng)氄?、讀共享。
- 讀寫鎖使用場合
- 讀寫鎖非常適合于對數(shù)據(jù)結(jié)構(gòu)讀的次數(shù)遠(yuǎn)大于寫的情況。
- 讀寫鎖特性
- 讀寫鎖是“寫模式加鎖”時(shí),解鎖前,所有對該鎖加鎖的線程都會被阻塞。
- 讀寫鎖是“讀模式加鎖”時(shí),如果線程以讀模式對其加鎖會成功;如果線程以寫模式加鎖會阻塞。
- 讀寫鎖是“讀模式加鎖”時(shí), 既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程。那么讀寫鎖會阻塞隨后的讀模式鎖請求。優(yōu)先滿足寫模式鎖。讀鎖、寫鎖并行阻塞,寫鎖優(yōu)先級高
- 讀寫鎖場景練習(xí):
- 線程A加寫鎖成功, 線程B請求讀鎖
- 線程B阻塞
- 線程A持有讀鎖, 線程B請求寫鎖
- 線程B阻塞
- 線程A擁有讀鎖, 線程B請求讀鎖
- 線程B加鎖成功
- 線程A持有讀鎖, 然后線程B請求寫鎖, 然后線程C請求讀鎖
- B阻塞,c阻塞?- 寫的優(yōu)先級高
- A解鎖,B線程加寫鎖成功,C繼續(xù)阻塞
- B解鎖,C加讀鎖成功
- 線程A持有寫鎖, 然后線程B請求讀鎖, 然后線程C請求寫鎖
- BC阻塞
- A解鎖,C加寫鎖成功,B繼續(xù)阻塞
- C解鎖,B加讀鎖成功
- 讀寫鎖總結(jié)
讀并行,寫?yīng)氄?#xff0c;當(dāng)讀寫同時(shí)等待鎖的時(shí)候?qū)懙膬?yōu)先級高
- 讀寫鎖主要操作函數(shù)
- 定義一把讀寫鎖
- pthread_rwlock_t rwlock;
- 初始化讀寫鎖
- int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
- 函數(shù)參數(shù)
- rwlock-讀寫鎖
- attr-讀寫鎖屬性,傳NULL為默認(rèn)屬性
- 銷毀讀寫鎖
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);????????
- 加讀鎖
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); ?????????????
- 嘗試加讀鎖
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
- 加寫鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
- 嘗試加寫鎖
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
- 解鎖
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);
- 練習(xí):3個(gè)線程不定時(shí)寫同一全局資源,5個(gè)線程不定時(shí)讀同一全局資源。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>//讀寫鎖測試程序
int number = 0;//定義一把讀寫鎖
pthread_rwlock_t rwlock;//讀寫鎖的回調(diào)函數(shù)
void *thread_write(void *arg)
{int i = *(int *)arg;int cur;while (1){//加讀寫鎖pthread_rwlock_wrlock(&rwlock);cur = number;cur++;number = cur;printf("[%d]-W:[%d]\n", i, cur);//解鎖pthread_rwlock_unlock(&rwlock);sleep(rand() % 3);}
}//讀回調(diào)函數(shù)
void *thread_read(void *arg)
{int i = *(int *)arg;int cur;while (1){//加讀鎖pthread_rwlock_rdlock(&rwlock);cur = number;printf("[%d]-R:[%d]\n", i, cur);//解鎖// pthread_rwlock_unlock(&rwlock);pthread_rwlock_unlock(&rwlock);sleep(rand() % 3);}
}int main()
{int n = 8;int i = 0;int arr[8];pthread_t thread[8];//讀寫鎖初始化pthread_rwlock_init(&rwlock, NULL);//創(chuàng)建3個(gè)寫子線程for (i = 0; i < 3; i++){arr[i] = i;pthread_create(&thread[i], NULL, thread_write, &arr[i]);}//創(chuàng)建5個(gè)讀子線程for (i = 3; i < n; i++){arr[i] = i;pthread_create(&thread[i], NULL, thread_read, &arr[i]);}//回收子線程int j = 0;for (j = 0; j < n; j++){pthread_join(thread[j], NULL);}//釋放鎖pthread_rwlock_destroy(&rwlock);system("pause");return 0;
}
-
3 條件變量
- 條件本身不是鎖!但它也可以造成線程阻塞。通常與互斥鎖配合使用。給多線程提供一個(gè)會合的場所。
- 使用互斥量保護(hù)共享數(shù)據(jù);
- 使用條件變量可以使線程阻塞, 等待某個(gè)條件的發(fā)生, 當(dāng)條件滿足的時(shí)候解除阻塞.
- 條件變量的兩個(gè)動作:
- 條件不滿足, 阻塞線程
- 條件滿足, 通知阻塞的線程解除阻塞, 開始工作.
- 條件變量相關(guān)函數(shù)
- pthread_cond_t ?cond;
- 定義一個(gè)條件變量
- int pthread_cond_init(pthread_cond_t *restrict cond,?const pthread_condattr_t *restrict attr);
- 函數(shù)描述:初始化條件變量
- 函數(shù)參數(shù):?
- 函數(shù)返回值:成功返回0, 失敗返回錯(cuò)誤號
- int pthread_cond_destroy(pthread_cond_t *cond);
- 函數(shù)描述:?銷毀條件變量
- 函數(shù)參數(shù):?條件變量
- 返回值:?成功返回0, 失敗返回錯(cuò)誤號
- int pthread_cond_wait(pthread_cond_t *restrict cond,
- 函數(shù)描述:?條件不滿足, 引起線程阻塞并解鎖;
- 函數(shù)參數(shù):
- 函數(shù)返回值: 成功返回0, 失敗返回錯(cuò)誤號
- ?int pthread_cond_signal(pthread_cond_t *cond);
- 函數(shù)描述: 喚醒至少一個(gè)阻塞在該條件變量上的線程
- 函數(shù)參數(shù): 條件變量
- 函數(shù)返回值: 成功返回0, 失敗返回錯(cuò)誤號
cond: 條件變量
attr: 條件變量屬性, 通常傳NULL
???????????????pthread_mutex_t *restrict mutex);
??????????條件滿足, 解除線程阻塞, 并加鎖
cond: 條件變量
mutex: 互斥鎖變量
使用條件變量的代碼片段
上述代碼中,生產(chǎn)者線程調(diào)用pthread_cond_signal函數(shù)會使消費(fèi)者線程在pthread_cond_wait處解除阻塞。
//使用條件變量實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>typedef struct node
{int data;struct node *next;
} NODE;NODE *head = NULL;//定義一把鎖
pthread_mutex_t mutex;//定義條件變量
pthread_cond_t cond;//生產(chǎn)者線程
void *producer(void *arg)
{NODE *pNode = NULL;while (1){pNode = (NODE *)malloc(sizeof(NODE));if (pNode == NULL){perror("malloc error");exit(-1);}pNode->data = rand() % 1000;printf("P:[%d]\n", pNode->data);//加鎖pthread_mutex_lock(&mutex);pNode->next = head;head = pNode;//解鎖pthread_mutex_unlock(&mutex);//通知消費(fèi)者線程解除阻塞pthread_cond_signal(&cond);sleep(rand() % 3);}
}//消費(fèi)者線程
void *consumer(void *arg)
{NODE *pNode = NULL;while (1){//加鎖pthread_mutex_lock(&mutex);if (head == NULL){//若條件不滿足,需要阻塞等待//若條件不滿足,則阻塞等待并解鎖;//若條件滿足(被生成者線程調(diào)用pthread_cond_signal函數(shù)通知),解除阻塞并加鎖pthread_cond_wait(&cond, &mutex);}printf("C:[%d]\n", head->data);pNode = head;head = head->next;//解鎖pthread_mutex_unlock(&mutex);free(pNode);pNode = NULL;sleep(rand() % 3);}
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//初始化互斥鎖pthread_mutex_init(&mutex, NULL);//條件變量初始化pthread_cond_init(&cond, NULL);//創(chuàng)建生產(chǎn)者線程ret = pthread_create(&thread1, NULL, producer, NULL);if (ret != 0){printf("pthread_create error,[%s]\n", strerror(ret));return -1;}//創(chuàng)建消費(fèi)者線程ret = pthread_create(&thread2, NULL, consumer, NULL);if (ret != 0){printf("pthread_create error,[%s]\n", strerror(ret));return -1;}//等待線程結(jié)束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//釋放互斥鎖pthread_mutex_destroy(&mutex);//釋放條件變量pthread_cond_destroy(&cond);system("pause");return 0;
}
多個(gè)生產(chǎn)者和消費(fèi)者
//使用條件變量實(shí)現(xiàn)多個(gè)生產(chǎn)者和消費(fèi)者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{int data;struct node *next;
}NODE;NODE *head = NULL;//定義一把鎖
pthread_mutex_t mutex;//定義條件變量
pthread_cond_t cond;//生產(chǎn)者線程
void *producer(void *arg)
{NODE *pNode = NULL;int n = *(int *)arg;while(1){//生產(chǎn)一個(gè)節(jié)點(diǎn)pNode = (NODE *)malloc(sizeof(NODE));if(pNode==NULL){perror("malloc error");exit(-1);}pNode->data = rand()%1000;printf("P[%d]:[%d]\n", n, pNode->data);//加鎖pthread_mutex_lock(&mutex);pNode->next = head;head = pNode;//解鎖pthread_mutex_unlock(&mutex);//通知消費(fèi)者線程解除阻塞pthread_cond_signal(&cond);sleep(rand()%3);}
}//消費(fèi)者線程
void *consumer(void *arg)
{NODE *pNode = NULL;int n = *(int *)arg;while(1){//加鎖pthread_mutex_lock(&mutex);if(head==NULL){//若條件不滿足,需要阻塞等待//若條件不滿足,則阻塞等待并解鎖;//若條件滿足(被生成者線程調(diào)用pthread_cond_signal函數(shù)通知),解除阻塞并加鎖 pthread_cond_wait(&cond, &mutex);}if(head==NULL){//解鎖pthread_mutex_unlock(&mutex); continue;}printf("C[%d]:[%d]\n", n, head->data); pNode = head;head = head->next;//解鎖pthread_mutex_unlock(&mutex);free(pNode);pNode = NULL;sleep(rand()%3);}
}int main()
{int ret;int i = 0;pthread_t thread1[5];pthread_t thread2[5];//初始化互斥鎖pthread_mutex_init(&mutex, NULL);//條件變量初始化pthread_cond_init(&cond, NULL);int arr[5];for(i=0; i<5; i++){arr[i]= i;//創(chuàng)建生產(chǎn)者線程ret = pthread_create(&thread1[i], NULL, producer, &arr[i]);if(ret!=0){printf("pthread_create error, [%s]\n", strerror(ret));return -1;}//創(chuàng)建消費(fèi)者線程ret = pthread_create(&thread2[i], NULL, consumer, &arr[i]);if(ret!=0){printf("pthread_create error, [%s]\n", strerror(ret));return -1;}}//等待線程結(jié)束for(i=0; i<5; i++){pthread_join(thread1[i], NULL);pthread_join(thread2[i], NULL);}//釋放互斥鎖pthread_mutex_destroy(&mutex);//釋放條件變量pthread_cond_destroy(&cond);return 0;
}
4 信號量
1 信號量介紹
信號量相當(dāng)于多把鎖, 可以理解為是加強(qiáng)版的互斥鎖
2 相關(guān)函數(shù)
定義信號量 sem_t sem; int sem_init(sem_t *sem, int pshared, unsigned int value);
pshared: 0表示線程同步, 1表示進(jìn)程同步
value: 最多有幾個(gè)線程操作共享數(shù)據(jù)
sem: 信號量變量
?3 信號量代碼片段:
- 函數(shù)描述: 初始化信號量
- 函數(shù)參數(shù):
- 函數(shù)返回值:成功返回0, 失敗返回-1, 并設(shè)置errno值
- int sem_wait(sem_t *sem);
- 函數(shù)描述: 調(diào)用該函數(shù)一次, 相當(dāng)于sem--, 當(dāng)sem為0的時(shí)候, 引起阻塞
- 函數(shù)參數(shù): 信號量變量
- 函數(shù)返回值: 成功返回0, 失敗返回-1, 并設(shè)置errno值
- int sem_post(sem_t *sem);
- 函數(shù)描述: 調(diào)用一次, 相當(dāng)于sem++
- 函數(shù)參數(shù): 信號量變量
- 函數(shù)返回值: 成功返回0, 失敗返回-1, 并設(shè)置errno值
- int sem_trywait(sem_t *sem);
- 函數(shù)描述: 嘗試加鎖, 若失敗直接返回, 不阻塞
- 函數(shù)參數(shù): 信號量變量
- 函數(shù)返回值: 成功返回0, 失敗返回-1, 并設(shè)置errno值
- int sem_destroy(sem_t *sem);
- 函數(shù)描述: 銷毀信號量
- 函數(shù)參數(shù): 信號量變量
- 函數(shù)返回值: 成功返回0, 失敗返回-1, 并設(shè)置errno值
//使用信號量實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
//信號量的頭文件
#include <semaphore.h>typedef struct node
{int data;struct node *next;
} NODE;NODE *head = NULL;//定義信號量
sem_t sem_producer;
sem_t sem_consumer;//生產(chǎn)者線程void *producer(void *arg)
{NODE *pNode = NULL;while (1){pNode = (NODE *)malloc(sizeof(NODE));if (pNode == NULL){perror("malloc error");exit(-1);}pNode->data = rand() % 100;printf("P;[%d]\n", pNode->data);//加鎖sem_wait(&sem_producer);pNode->next = head;head = pNode;//解鎖sem_post(&sem_consumer);sleep(rand() % 3);}
}void *consumer(void *arg)
{NODE *pNode = NULL;while (1){//加鎖sem_wait(&sem_consumer); //相當(dāng)于--printf("C:[%d]\n", head->data);pNode = head;head = head->next;//解鎖sem_post(&sem_producer); //相當(dāng)于++free(pNode);pNode = NULL;sleep(rand() % 3);}
}int main()
{int ret;pthread_t thread1;pthread_t thread2;//初始化信號量sem_init(&sem_producer, 0, 5);sem_init(&sem_consumer, 0, 0);//創(chuàng)建生產(chǎn)者線程ret = pthread_create(&thread1, NULL, producer, NULL);if (ret != 0){printf("pthread create error,[%s]\n", strerror(ret));return -1;}ret = pthread_create(&thread2, NULL, consumer, NULL);if (ret != 0){printf("pthread create error,[%s]\n", strerror(ret));return -1;}//等待線程結(jié)束pthread_join(thread1, NULL);pthread_join(thread2, NULL);//釋放信號量資源sem_destroy(&sem_producer);sem_destroy(&sem_consumer);system("pause");return 0;
}