1. 互斥量
????在Linux 下線程使用的都是局部變量, 而我們知道, 每一個線程都獨立擁有自己的一個棧, 而這些局部便令就在棧中,而線程的創建就是為了實現通信, 此時線程之間無法共享這些變量
????為了使得線程之間能夠共享數據, 一次我們可以創建一個全局變量, 此時線程都在進程內部共享一個地址空間, 因此個線程之間就可以看到這個全局變量了
????但是問題又來了, 創建了全局變量, 線程之間其實看到了一份公共資源, 而此時一個線程之間由于共同訪問這個局部變量很有可能造成線程之間的不安全, 為了使得線程之間能夠正確訪問, 我們就引入了互斥量.我們規定,當代碼進入臨界區執行的時候, 不允許其他線程進入該臨界區. 當有多個線程要求執行臨界區的代碼時,此時如果臨界區沒有如何線程的時候, 操作系統只允許一個線程進入該臨界區, 而其他的線程則必須在臨界區外等待, 直到進入臨界區的線程走出臨界區, 并且釋放互斥量.如果線程不在臨界區內, 該線程不能組織其他線程進入臨界區
2.互斥量相關接口
1.初始化互斥量
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t* restict attr);參數:mutex:要初始化的互斥量attr: NULL
2.銷毀互斥量
int pthread_mutex_destroy(pthread_mutex_t* mutex);
3.互斥量的加鎖
int pthread_mutex_lock(pthrea_mutex_t* mutex);
4.互斥量的解鎖
int pthread_mutex_unlock(pthread_mutex_t* mutex);
????注意, 互斥量處于互斥狀態時, 加鎖函數會將該互斥量鎖定, 同時返回成功, 當發起加鎖函數被調用的時候, 其他線程已經鎖定互斥量, 或者當好多線程同時申請互斥量的時候, 此時線程之間就在相互競爭這個互斥量,此時, 該函數調用將會陷入阻塞狀態,一直等待該互斥量, 直到擁有該互斥量的線程主動釋放該互斥鎖
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>int ticket = 100;
pthread_mutex_t mutex;
void* route(void* arg)
{char* id = (char*)arg;while(1){//pthread_mutex_lock(&mutex);if(ticket > 0){usleep(10000);printf("%s sells ticket:%d\n", id, ticket);ticket--;//pthread_mutex_unlock(&mutex);}else{//pthread_mutex_unlock(&mutex);break;}}
}
int main()
{pthread_t t1, t2, t3, t4;pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, route, "thread 1");pthread_create(&t2, NULL, route, "thread 2");pthread_create(&t3, NULL, route, "thread 3");pthread_create(&t4, NULL, route, "thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);return 0;
}
???????????????????????????????????
????由上圖可以看出, 由于我們未加互斥鎖, 線程對數據的不正確操作造成了數據的錯誤,當我我們在線程進入臨界區的時候臨界資源加上互斥鎖, 而在線程離開臨界區的時候對互斥量進行釋放, 此時就不會出現數據錯誤的現象了
???????????????????????????????????
3. 條件變量
????當一個線程互斥訪問某個變量時, 它可能發現在其他線程改變狀態的之前它什么也不能做, 例如一個線程訪問隊列時, 它發現這個隊列為空, 此時,它只能等待, 直到其他線程將結點放到該隊列.這時, 為了使得各個線程之間能夠同步的訪問這個變量,此時就需要有一個變量, 該變量必須要符合某個條件時, 線程才能訪問這個變量.
4. 相關接口
1. 初始化
int pthread_cond_init(pthread_cond_t* restrict cond, const pthread_condattr_t* restrict attr);cond: 表示要初始化的條件變量attr: NULL
2. 銷毀
int pthread_cond_destroy(pthread_cond_t* cond)
3. 等待條件滿足
int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex);cond: 如果條件不滿足, 就在條件變量上等待mutex: 互斥量, 表名線程退出時必須釋放哪一個鎖
4. 喚醒
int pthread_cond_broadcast(pthread_cond_t* cond);用來喚醒一群線程
int pthread_cond_signal(pthread_cond_t* cond);用來喚醒某個線程
????也許你會問, 既然線程等待, 那就直接等待就好了, 有一個條件變量就可以了, 但是為什么好要有一個互斥量呢?通過上面所述, 我們知道, 條件變量是為了使得線程之間得到同步所采用的一種手段. 當只有一個線程的時候, 條件不滿足, 此時線程等待, 一直等待下去, 此時由于只有一個線程, 該線程所等待的條件將始終不會得到滿足, 一次線程將會一直等待下去. 所以, 為了能夠及時通知等待的線程, 必須有另外的線程對等待的這個條件(共享變量)做出預定的操作, 使得等待的線程所等待的條件得到滿足, 此時, 這個條件變量是一個共享變量, 為了使得每一個線程訪問該數據的正確性, 此時就必須引入互斥量來保證臨界資源的正確性.
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>pthread_cond_t cond;
pthread_mutex_t mutex;void* thread1(void* arg)
{while(1){printf("%s\n", (char*)arg);pthread_cond_wait(&cond, &mutex);printf("活動\n");}
}void* thread2(void* arg)
{while(1){sleep(2);printf("%s\n", (char*)arg);pthread_cond_signal(&cond);}
}
int main()
{pthread_t t1;pthread_t t2;pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, thread1, "thread1");pthread_create(&t2, NULL, thread2, "thread2");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_cond_destroy(&cond);pthread_cond_destroy(&cond);return 0;
}
????上面代碼是用來創建線程1, 線程2, 同時線程在條件變量上進行等待, 直到有線程將其喚醒, 線程2睡眠一秒, 然后將等待 cond 的線程1喚醒, 此時線程2被喚醒, 它開始執行, 執行完之后,它有開始等待條件變量 cond直到條件變量 cond 滿足時再醒來.
???????????????????????????????????
????通過上圖可以看出來, 線程2要活動, 它必須等待條件變量 cond 滿足時才能活動, 即線程2 必須在條件變量上等待, 直到線程 2 將其喚醒