線程同步的了解
當多個控制線程共享相同的記憶體時,需要確定每個線程看到一緻的資料視圖。如果每個線程使用的變量都是其他線程不會讀取或修改的,那麼就不存在一緻性問題。同樣地,如果變量是隻讀的,多個線程同時讀取該量也不會有一緻性問題。但是,當某個線程可以修改變量,而其他線程也可以讀取或者修改這個變量的時候,就需要對這些線程進行同步,以確定它們在通路變量的存儲内容時不會通路到無效的資料。
互斥量的概念
可以通過使用pthread的互斥接口保護資料,確定同一時間隻有一個線程通路資料。
1、 互斥量從本質上說是一把鎖,在通路共享資源前對互斥量進行加鎖,在通路完成之後釋放互斥量上的鎖。對互斥量進行加鎖以後,任何其他試圖再次對互斥量加鎖的線程将會被阻塞直到目前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運作狀态,第一個變為運作狀态的線程可以對互斥量加鎖,其他線程将會看到互斥鎖依然被鎖住,隻能回去再次等待它重新變為可用。在這種方式下,每次隻能有一個線程可以向前執行。
2、互斥鎖是用來實作線程通路臨界資源的同步控制。如果一個線程在臨界區開始時,給互斥鎖加鎖,那麼其他的線程就必須等待線程解鎖,才能接着運作,并通路資源。
互斥量的使用
1、互斥量的初始化和銷毀
互斥變量用pthread_mutex_t資料類型來表示,在使用互斥變量以前,必須首先對它進行初始化,可以把它置為常量PTHREAD_MUTEX_INITIALIZER(隻對靜态配置設定的互斥量),也可以通過調用pthread_mutex_init函數進行初始化。如果動态地配置設定互斥量(例如通過調用malloc函數),那麼在釋放記憶體前需要調用pthread_mutex_destroy函數。
鎖的初始化和銷毀函數的原型如下:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
傳回值:若成功則傳回0,否則傳回錯誤編号。
要用預設的屬性初始化互斥量,隻需把attr設定為NULL。
非預設的互斥量屬性有:程序共享屬性和類型屬性。
2、互斥量的加鎖和解鎖
對互斥量進行加鎖,需要調用pthread_mutex_lock函數,如果互斥量已經上鎖,調用線程将阻塞直到互斥量被解鎖,需要調用pthread_mutex_unlock函數。
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutext_unlock(pthread_mutex_t *mutex);
傳回值:若成功則傳回0,否則傳回錯誤編号。
如果線程不希望被阻塞,它可以使用pthread_mutex_trylock函數嘗試對互斥量進行加鎖。如果調用pthread_mutex_trylock時互斥量處于未鎖住狀态,那麼pthread_mutex_trylock将鎖住互斥量,不會出現阻塞并傳回0,否則pthread_mutex_trylock就會失敗,不會鎖住互斥量,而傳回EBUSY。
3、代碼舉例
(1)主線程負責接收使用者輸入,函數線程統計使用者輸入的字元個數。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
char buf[] = {};//定義全局臨界資源
pthread_mutex_t p;//定義全局互斥量
//函數線程
void * fun_pthread1(void *arg)
{
while()
{
pthread_mutex_lock(&p);//給互斥量加鎖
if(strncmp(buf,"end",) == )//當從buf中存放的是"end"時,就會跳出重複讀取buf的内容,函數線程也會結束
{
break;
}
int count = ;
while()
{
if(buf[count] == || buf[count] == '\n')
{
break;
}
count++;
}
printf("count:%d\n",count);//列印主線程每次輸入的字元個數
pthread_mutex_unlock(&p);//給互斥量解鎖
sleep();
}
}
int main()
{
pthread_mutex_init(&p,NULL);//給互斥量進行初始化,第二個參數是屬性,NULL表示使用預設屬性
pthread_mutex_lock(&p);//給互斥量加鎖
pthread_t id;//線程id
pthread_create(&id,NULL,fun_pthread1,NULL);//建立函數線程,第二個參數是屬性,NUll表示使用預設屬性,第四個參數傳遞的是參數,NULL表示沒有參數
while()
{
printf("請輸入:\n");
fgets(buf,,stdin);//主線程向buf中輸入内容
pthread_mutex_unlock(&p);//釋放鎖
if(strncmp(buf,"end",)==)//當主線程輸入"end"時,主線程就會跳出循環輸入,不會列印輸入的字元個數
{
break;
}
sleep();
pthread_mutex_lock(&p);
}
pthread_join(id,NULL);//等待函數線程結束再執行,第二個參數接收函數線程結束時傳遞的資訊,NULl表示無資訊。
pthread_mutex_destroy(&p);//當函數線程結束時,銷毀互斥量
// pthread_exit(NULL);//主線程退出時,函數線程不一定結束,如果使用exit函數(預設)退出,那麼程序就會結束,所有包含在程序中的線程也會結束,可能導緻函數線程未執行完突然結束
return ;
}
結果展示:
(2)實作主線程循環接收使用者輸入,以“end”作為結束标志,函數線程統計使用者每次輸入的單詞個數,最後再統計總輸入的單詞個數。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
char buf[] = {};//臨界資源
pthread_mutex_t p;//互斥量
int sum = ;
//函數線程
void * fun_pthread1(void *arg)
{
while()
{
pthread_mutex_lock(&p);
if(strncmp(buf,"end",) == )//比較兩個字元串的前3個字元
{
break;//結束循環
}
int flag = ;
int count = ;
int len = strlen(buf);
int i;
for(i=;i<len;++i)
{
// 判斷是不是空格,是的話flag=1
// 不是的話,判斷前面是不是空格即flag是否等于1
// 是空格的話,說明是新單詞的開始
if(buf[i] == '\n')
{
break;
}
if(buf[i] == ' ')
{
flag = ;
}
else
{
if(buf[i] < 'A' || (buf[i] > 'Z' && buf[i] < 'a') || buf[i] > 'z') //如果是非字母字元就略過,不計入單詞個數中
{
continue;
}
if(flag == )
{
count++;
flag = ;
}
}
}
printf("count:%d\n",count);//列印本次輸入的單詞個數
sum += count;
pthread_mutex_unlock(&p);
sleep();
}
}
int main()
{
pthread_mutex_init(&p,NULL);//第二個參數是屬性,使用預設屬性
pthread_mutex_lock(&p);
pthread_t id;
pthread_create(&id,NULL,fun_pthread1,NULL);//第二個參數是屬性,使用預設屬性,第四個參數傳遞的是參數,NULL表示無
while()
{
printf("請輸入:\n");
fgets(buf,,stdin);
pthread_mutex_unlock(&p);
if(strncmp(buf,"end",)==)
{
break;
}
sleep();
pthread_mutex_lock(&p);
}
pthread_join(id,NULL);
pthread_mutex_destroy(&p);
printf("sum = %d\n",sum);//列印總輸入的單詞個數
return ;
}
結果示範: