一、互斥鎖
1. 函數原型:
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_destroy(pthread_mutex_t *mutex);
分析:
- pthread_mutex_t 類型,其本質是一個結構體,為簡化理解,應用時可忽略其實現細節,簡單當成整數看待。
- pthread_mutex_t??mutex:變量mutex只有兩種取值0、1;
函數一參數1:傳出參數,調用時應傳&mutex
- restrict關鍵字:只用于限制指針,告訴編譯器,所有修改該指針指向內存中內容的操作,只能通過本指針完成。不能通過除本指針以外的其他變量或指針修改。
函數一參數2:互斥屬性。是一個傳入參數,通常傳NULL,選用默認屬性(線程間共享).
- 靜態初始化:如果互斥鎖mutex是靜態分配的(定義在全局,或加了static關鍵字修飾),可以直接使用宏進行初始化。pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 動態初始化:局部變量應采用動態初始化, pthread_mutex_init(&mutex, NULL);
?
pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock(pthread_mutex_t *mutex);
分析:
- 函數1:沒有被上鎖,當前線程會將這把鎖鎖上;被鎖上了,當前線程阻塞,鎖被打開之后,線程解除阻塞(加鎖。可理解為將mutex--(或-1))。
- 函數2:同時將阻塞在該鎖上的所有線程全部喚醒解鎖(可理解為將mtex++(或+1)).
?
2. 測試代碼:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *tfn(void *arg)
{srand(time(NULL));while(1) {printf("hello ");sleep(rand() % 3); //模擬長時間操作共享資源,導致cpu易主,產生與時間有關的錯誤printf("word\n");sleep(rand() % 3);}return NULL;
}int main()
{pthread_t tid;srand(time(NULL));pthread_create(&tid, NULL, tfn, NULL);while(1) {printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");sleep(rand() % 3);}return 0;
}
輸出結果:
?
??3. 測試代碼:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *tfn(void *arg)
{srand(time(NULL));while(1) {printf("hello ");sleep(rand() % 3); //模擬長時間操作共享資源,導致cpu易主,產生與時間有關的錯誤printf("word\n");sleep(rand() % 3);}return NULL;
}int main()
{pthread_t tid;srand(time(NULL));pthread_create(&tid, NULL, tfn, NULL);while(1) {printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");sleep(rand() % 3);}return 0;
}
輸出結果
?
二、 條件變量
條件變量本身不是鎖,但它也可以造成線程阻塞,通常與互斥鎖配合使用,給多線程提供一個會合的場所。
?
1. 函數原型:
pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t * attr); //初始化一個條件變量
pthread_cond_destroy(pthread_cond_t *cond); // 銷毀一個條件變量 pthread_cond_signal(pthread_cond_t *cond); // 喚醒至少一個阻塞在條件變量上的線程
pthread_cond_broadcast(pthread_cond_t *cond); // 喚醒全部阻塞在條件變量上的線程
?
2. 函數原型:
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
分析:
函數作用:阻塞等待一個條件變量
- 阻塞等待條件變量cond(參數1)滿足
- 釋放已掌握的互斥鎖(解鎖互斥量),相當于pthread_mutex_unlock(&mutex); {1、2兩步為一個原子操作}
- 當被喚醒,pthread_cond_wait函數返回,解除阻塞并重新獲取互斥鎖pthread_mutex_lock(&mutex);
?
3. 函數原型:
? ??函數作用:限時等待一個條件變量
pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
參數3:
struct timespec
{time_t tv_sec; seconds 秒 long tv_nsec; nanoseconds 納秒
}
形參:abstime:絕對時間
如:time(NULL)返回的就是絕對時間。而alarm(1)是相對時間,相當當前時間定時1秒鐘。
struct timespec t = {1,??0};
pthread_cond_time(&cond, &mutex, &t);只能定時到1970年1月1日00:00:01秒(早已經過去)
正確用法:
time_t cur = time(NULL); 獲取當前時間
struct timespec t; 定義timespec結構體變量
t.tv_sec = cur + 1; 定時一秒
pthread_cond_timewait(&cond, mutex, &t) 傳參
?
4. 函數原型:
pthread_cond_signal(pthread_cond_t *cond); // 喚醒至少一個阻塞在條件變量上的線程
pthread_cond_broadcast(pthread_cond_t *cond); // 喚醒全部阻塞在條件變量上的線程
?
?
三、生產者消費者條件變量模型
線程同步典型的案例即為生產者消費者模型,而借助條件變量來實現這一案例,是比較常見的方法,假定有兩個線程,一個模擬生產者行為,一個模擬消費者行為,兩個線程同時操作一個共享資源(一般稱之為匯聚),生產者向其中添加產品,消費者從中消費掉產品。
1. 測試代碼:
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>struct msg
{struct msg *next;int num;
};struct msg *head;
struct msg *mp;pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; //靜態初始化:一個條件變量和一個互斥量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{for (; ;){pthread_mutex_lock(&lock);while (head == NULL) //頭指針為空,說明沒有節點 可以為if嗎 pthread_cond_wait(&has_product, &lock);mp = head;head = mp->next; //模擬消費掉一個產品pthread_mutex_unlock(&lock);printf("-consume-----%d\n", mp->num);free(mp);sleep(rand() % 5);}
}void *producer(void *p)
{for (; ;) {mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1; //模擬生產一個產品printf("-Produce----%d\n", mp->num);pthread_mutex_lock(&lock);mp->next = head;head = mp;pthread_mutex_unlock(&lock);pthread_cond_signal(&has_product); //將等待在該條件變量上的一個線程喚醒sleep(rand() % 5);}
}int main()
{pthread_t pid, cid;srand(time(NULL));pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
輸出結果:
?【注意】:為什么用while循環而不用if呢?
一個生產者可能對應著多個消費者,生產者向隊列中插入一條數據之后發出signal,然后各個消費者線pthread_cond_wait獲取mutex后返回,當然,這里只有一個線程獲取到了mutex,然后進行處理,其它線程會pending在這里,處理線程處理完畢之后釋放mutex,剛才等待的線程中有一個獲取mutex,如果這里用if,就會在當前隊列為空的狀態下繼續往下處理,這顯然是不合理的。
?
?
五、參考資料
1?深入解析條件變量(condition variables)
?