線程同步(同步的意思是協同步調)
線程同步機制包括互斥,讀寫鎖以及條件變量等
3.2.1 互斥量(互斥鎖)
**互斥量本質是一把鎖,在通路公共資源前對互斥量設定(加鎖),確定同一時間隻有一個線程通路資料,在通路完成後再釋放(解鎖)互斥量。**在互斥量加鎖之後,其他線程試圖對該互斥量再次加鎖時都會被阻塞,知道目前線程釋放互斥鎖。如果釋放互斥量時有一個以上的互斥量,那麼所有在該互斥量上阻塞的線程都會變成可運作狀态,第一個變成運作的線程可以對互斥量加鎖(哪個線程先運作哪個線程就可以對互斥量加鎖),其他線程看到互斥量依然是鎖着的,隻能再次阻塞等待該互斥量。
互斥量用pthread_mutex_t資料類型表示,在使用互斥量之前,必須使用pthread_mutex_init函數對它進行初始化,注意,使用完畢後需調用pthread_mutex_destroy。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 兩個函數傳回值,成功傳回0,否則傳回錯誤碼
pthread_mutex_init用于初始化互斥鎖,mutexattr用于指定互斥鎖的屬性,若為NULL,則表示預設屬性。除了用這個函數初始化互斥所外,還可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用于銷毀互斥鎖,以釋放占用的核心資源,銷毀一個已經加鎖的互斥鎖将導緻不可預期的後果。
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 成功傳回0,否則傳回錯誤碼
pthread_mutex_lock以原子操作給一個互斥鎖加鎖。如果目标互斥鎖已經被加鎖,則pthread_mutex_lock則被阻塞,直到該互斥鎖占有者把它給解鎖。
pthread_mutex_trylock和pthread_mutex_lock類似,不過它始終立即傳回,而不論被操作的互斥鎖是否加鎖,是pthread_mutex_lock的非阻塞版本。當目标互斥鎖未被加鎖時,pthread_mutex_trylock進行加鎖操作;否則将傳回EBUSY錯誤碼。注意:這裡讨論的pthread_mutex_lock和pthread_mutex_trylock是針對普通鎖而言的,對于其他類型的鎖,這兩個加鎖函數會有不同的行為。
pthread_mutex_unlock以原子操作方式給一個互斥鎖進行解鎖操作。如果此時有其他線程正在等待這個互斥鎖,則這些線程中的一個将獲得它。
互斥量執行個體
/**
* 使用3個線程分别列印 A B C
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
pthread_mutex_t g_mutex;
int g_cnt = 0;
void *func(void *arg)
{
int loop = 3;
int result = (int)arg;
printf("result=%d\n",result);
//while (loop > 0) {
//if (g_cnt % 3 == result) {
switch (result)
{
case 0: {
printf("--- a\n");
break;
}
case 1: {
printf("--- b\n");
break;
}
case 2: {
printf("--- c\n");
break;
}
default: {
return NULL;
}
}
pthread_mutex_lock(&g_mutex);
g_cnt++;
//loop--;
printf("g_cnt=%d\n",g_cnt);
pthread_mutex_unlock(&g_mutex);
// }
//}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t t1, t2, t3;
pthread_mutex_init(&g_mutex, NULL);
pthread_create(&t1, NULL, func, (void *)0);
pthread_create(&t2, NULL, func, (void *)1);
pthread_create(&t3, NULL, func, (void *)2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_mutex_destroy(&g_mutex);
return 0;
}
3.2.2 條件變量
條件變量是線程可用的一種同步機制,條件變量給多個線程提供了一個回合的場所,條件變量和互斥量一起使用,允許線程以無競争的方式等待特定的條件發生。條件變量本身是由互斥體保護的,線程在改變條件狀态之前必須首先鎖住互斥量,其他線程在擷取互斥量之前就不會覺察到這種變化,因為互斥量必須鎖定之後才改變條件。
#include<pthread.h>
pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
pthread_cond_destroy(pthread_cont_t *cond);
// 成功傳回0,否則傳回錯誤碼
使用條件變量前調用pthread_cond_init初始化,使用完畢後調用pthread_cond_destroy做清理工作。除非需要建立一個具有非預設屬性的條件變量,否則pthread_cond_init函數的attr參數可以設定為NULL。
#include<pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
// 成功傳回0,否則傳回錯誤碼
傳遞給pthread_cond_wait的互斥量對條件進行保護,調用者把鎖住互斥量傳給函數,函數然後自動把調用線程放到等待條件的線程清單上,對互斥量解鎖。這就關閉了條件檢查和線程進入休眠狀态等待條件改變這兩個操作之間的時間通道,這樣線程就不會錯過條件的任何變化。pthread_cond_wait函數傳回時,互斥量再次被鎖住。
pthread_cond_broadcast用廣播的形式喚醒所有等待條件變量的線程。pthread_cond_signal用于喚醒一個等待條件變量的線程,至于哪個線程被喚醒,取決于線程的優先級和排程機制。有時候需要喚醒一個指定的線程,但pthread沒有對該需要提供解決方法。可以間接實作該需求:定義一個能夠唯一表示目标線程的全局變量,在喚醒等待條件變量的線程前先設定該變量為目标線程,然後以廣播形式喚醒所有等待條件變量的線程,這些線程被喚醒後都檢查改變量是否是自己,如果是就開始執行後續代碼,否則繼續等待。
條件變量執行個體
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define err_sys(msg) \
do { perror(msg); exit(-1); } while(0)
#define err_exit(msg) \
do { fprintf(stderr, msg); exit(-1); } while(0)
pthread_cond_t cond;
void *r1(void *arg)
{
pthread_mutex_t* mutex = (pthread_mutex_t *)arg;
static int cnt = 10;
while(cnt--)
{
printf("r1: I am wait.\n");
pthread_mutex_lock(mutex);
pthread_cond_wait(&cond, mutex); /* mutex參數用來保護條件變量的互斥鎖,調用pthread_cond_wait前mutex必須加鎖 */
pthread_mutex_unlock(mutex);
}
return "r1 over";
}
void *r2(void *arg)
{
pthread_mutex_t* mutex = (pthread_mutex_t *)arg;
static int cnt = 10;
while(cnt--)
{
//pthread_mutex_lock(mutex); //這個地方不用加鎖操作就行
printf("r2: I am send the cond signal.\n");
pthread_cond_signal(&cond);
//pthread_mutex_unlock(mutex);
sleep(1);
}
return "r2 over";
}
int main(void)
{
pthread_mutex_t mutex;
pthread_t t1, t2;
char* p1 = NULL;
char* p2 = NULL;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&t1, NULL, r1, &mutex);
pthread_create(&t2, NULL, r2, &mutex);
pthread_join(t1, (void **)&p1);
pthread_join(t2, (void **)&p2);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
printf("s1: %s\n", p1);
printf("s2: %s\n", p2);
return 0;
}