臨界區(qū)
- 臨界區(qū)是指必須以互斥的方式執(zhí)行的代碼段骂际,也就是說(shuō)臨界區(qū)范圍內(nèi)只能有一個(gè)活動(dòng)的線程。例如:修改共享變量的過(guò)程中其他的執(zhí)行線程可能會(huì)訪問(wèn)共享變量,那么修改共享變量的代碼就被看成是臨界區(qū)的一部分。
- 臨界區(qū)管理指用安全、公平和對(duì)稱的方式來(lái)執(zhí)行臨界區(qū)代碼的方法民珍。
- 臨界區(qū)管理的基本原則
①如果有若干進(jìn)程要求進(jìn)入空閑的臨界區(qū)襟士,一次僅允許一個(gè)進(jìn)程進(jìn)入。
②任何時(shí)候嚷量,處于臨界區(qū)內(nèi)的進(jìn)程不可多于一個(gè)陋桂。如已有進(jìn)程進(jìn)入自己的臨界區(qū),則其它所有試圖進(jìn)入臨界區(qū)的進(jìn)程必須等待蝶溶。
③進(jìn)入臨界區(qū)的進(jìn)程要在有限時(shí)間內(nèi)退出嗜历,以便其它進(jìn)程能及時(shí)進(jìn)入自己的臨界區(qū)。
④如果進(jìn)程不能進(jìn)入自己的臨界區(qū)身坐,則應(yīng)讓出CPU秸脱,避免進(jìn)程出現(xiàn)“忙等”現(xiàn)象。
互斥與同步
- 互斥
1.互斥部蛇,是指在不同進(jìn)程/線程之間的若干程序片斷摊唇,當(dāng)某個(gè)進(jìn)程/線程運(yùn)行其中一個(gè)程序片段時(shí),其它進(jìn)程/線程就不能運(yùn)行它 們之中的某個(gè)程序片段涯鲁,只能等到該進(jìn)程/線程運(yùn)行完這個(gè)程序片段后才可以運(yùn)行巷查。
2.互斥是指某一資源同時(shí)只允許一個(gè)訪問(wèn)者對(duì)其進(jìn)行訪問(wèn),具有唯一性和排它性抹腿。但互斥無(wú)法限制訪問(wèn)者對(duì)資源的訪問(wèn)順序岛请,即訪問(wèn)是無(wú)序的。 - 同步
1.同步警绩,是在不同進(jìn)程/線程之間的若干程序片斷崇败,它們的運(yùn)行必須嚴(yán)格按照規(guī)定的某種先后次序來(lái)運(yùn)行,這種先后次序依賴于要完成的特定的任務(wù)肩祥。
2.同步是指在互斥的基礎(chǔ)上(大多數(shù)情況)后室,通過(guò)其它機(jī)制實(shí)現(xiàn)訪問(wèn)者對(duì)資源的有序訪問(wèn),具有順序性混狠。
3.可以說(shuō)同步是更為復(fù)雜的互斥
多線程的同步與互斥
- 由于線程共享進(jìn)程的資源和地址空間岸霹,因此在對(duì)這些資源進(jìn)行操作時(shí),必須考慮到線程間資源訪問(wèn)的同步與互斥問(wèn)題将饺。
- POSIX中兩種線程同步與互斥機(jī)制贡避,分別為互斥鎖和信號(hào)量。這兩個(gè)同步與互斥機(jī)制可以互相通過(guò)調(diào)用對(duì)方來(lái)實(shí)現(xiàn)予弧,也就是相互可以實(shí)現(xiàn)對(duì)方的功能刮吧,但互斥鎖更適合用于同時(shí)可用的資源是惟一的情況;信號(hào)量更適合用于同時(shí)可用的資源為多個(gè)的情況掖蛤。
互斥鎖線程控制
- 互斥鎖是用一種簡(jiǎn)單的加鎖方法來(lái)控制對(duì)共享資源的原子操作杀捻。
- 互斥鎖只有兩種狀態(tài):上鎖和解鎖。
- 在同一時(shí)刻只能有一個(gè)線程掌握某個(gè)互斥鎖坠七,擁有上鎖狀態(tài)的線程能夠?qū)蚕碣Y源進(jìn)行操作水醋。若其他線程希望上鎖一個(gè)已經(jīng)被上鎖的互斥鎖,則該線程就會(huì)掛起彪置,直到上鎖的線程釋放掉互斥鎖為止拄踪。
- 這把互斥鎖保證讓每個(gè)線程對(duì)共享資源按順序進(jìn)行原子操作。
- 互斥鎖機(jī)制主要包括下面的基本函數(shù)拳魁。
互斥鎖初始化:pthread_mutex_init()
互斥鎖上鎖:pthread_mutex_lock()
互斥鎖判斷上鎖:pthread_mutex_trylock()
互斥鎖解鎖:pthread_mutex_unlock()
消除互斥鎖:pthread_mutex_destroy()
函數(shù)pthread_mutex_trylock是pthread_mutex_lock的非阻塞版本惶桐,表示嘗試加鎖,如果該互斥鎖已經(jīng)鎖定潘懊,則返回一個(gè)不為0的錯(cuò)誤值姚糊,如果該互斥鎖沒(méi)有鎖定,則返回0授舟,表示嘗試加鎖成功
- 互斥鎖可以分為快速互斥鎖救恨、遞歸互斥鎖和檢錯(cuò)互斥鎖。
1.快速鎖是指調(diào)用線程會(huì)阻塞直至擁有互斥鎖的線程解鎖為止释树。
2.遞歸互斥鎖能夠成功地返回肠槽,并且增加調(diào)用線程在互斥上加鎖的次數(shù)。
3.檢錯(cuò)互斥鎖則為快速互斥鎖的非阻塞版本奢啥,它會(huì)立即返回并返回一個(gè)錯(cuò)誤信息秸仙。
默認(rèn)屬性為快速互斥鎖。 - 這三種鎖的區(qū)別主要在于其他未占有互斥鎖的線程在希望得到互斥鎖時(shí)是否需要阻塞等待桩盲。
- 互斥鎖代碼實(shí)戰(zhàn)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>//調(diào)用linux線程函數(shù)必須包含此頭文件
#define THREAD_NUMBER 3
#define REPEAT_NUMBER 3
#define DELAY_TIME_LEVELS 3.0
pthread_mutex_t mutex;
void * thrd_func(void *arg)//子線程入口函數(shù)
{
int thrd_num = (int)arg; //主線程對(duì)每個(gè)線程自定義了一個(gè)編號(hào)寂纪,放在參數(shù)arg中
int delay_time = 0;
int count = 0;
pthread_mutex_lock(&mutex);//獲取互斥鎖
printf("Thread %d is starting\n", thrd_num);
for (count = 0; count < REPEAT_NUMBER; count++)
{
//rand()為隨機(jī)數(shù)產(chǎn)生函數(shù),其隨機(jī)數(shù)的最大值為RAND_MAX(系統(tǒng)定義)赌结,
//(int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX))將會(huì)得到一個(gè)0——9之間的隨機(jī)數(shù),注意//強(qiáng)制類型轉(zhuǎn)換int不能世痰啊!姑曙!
delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);//睡眠
printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);
}
printf("Thread %d finished\n", thrd_num);
pthread_mutex_unlock(&mutex);//釋放鎖
pthread_exit(NULL);//線程退出襟交,這里也可以不要此語(yǔ)句,讓線程“自然死亡”伤靠!
}
int main(void)//主線程入口
{
pthread_t thread[THREAD_NUMBER];//聲明此數(shù)組用來(lái)保存子線程號(hào)
int no = 0, res;
void * thrd_ret;
srand(time(NULL));//用當(dāng)前時(shí)間作為一個(gè)隨機(jī)數(shù)種子捣域,否則隨機(jī)數(shù)序列每次都一樣!
pthread_mutex_init(&mutex,NULL);
for (no = 0; no < THREAD_NUMBER; no++)
{
//no的值將傳遞給線程入口函數(shù)
res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);//創(chuàng)建子線程
// sleep(1);
if (res != 0)
{
printf("Create thread %d failed\n", no);
exit(res);
}
else
{
printf("Create thread %d success\n", no);
}
}
printf("Create treads success\n Waiting for threads to finish...\n");
for (no = 0; no < THREAD_NUMBER; no++)
{
//按創(chuàng)建順序逐個(gè)等待子線程結(jié)束
res = pthread_join(thread[no], &thrd_ret);
if (!res)
{
printf("Thread %d joined\n", no);
}
else
{
printf("Thread %d join failed\n", no);
}
}
pthread_mutex_destroy(&mutex);
return 0;
}
信號(hào)量線程控制
POSIX信號(hào)量是操作系統(tǒng)中所用到的PV原子操作宴合,它廣泛用于進(jìn)程或線程間的同步與互斥焕梅。
信號(hào)量本質(zhì)上是一個(gè)非負(fù)的整數(shù)計(jì)數(shù)器,它被用來(lái)控制對(duì)公共資源的訪問(wèn)卦洽。
- PV原子操作是對(duì)非負(fù)整數(shù)信號(hào)量sem的操作
- P操作
判斷sem是否大于0
如果是就執(zhí)行sem=sem-1;訪問(wèn)資源
否則就阻塞線程贞言,直到sem大于0為止 -
V操作
判斷在該信號(hào)量隊(duì)列中是否有被阻塞的線程
如果有就喚醒排在第一的阻塞線程,sem=sem+1
否則就只執(zhí)行sem=sem+1;
- P操作
- 信號(hào)量操作函數(shù)
sem_init()//用于創(chuàng)建一個(gè)信號(hào)量阀蒂,并初始化它的值该窗。
sem_wait()//和sem_trywait()都相當(dāng)于P操作弟蚀,在信號(hào)量大于零時(shí)它們都能將信號(hào)量的值減一,兩者的區(qū)別在于若信號(hào)量等于零時(shí)酗失,sem_wait()將會(huì)阻塞進(jìn)程义钉,而sem_trywait()//則會(huì)立即返回。
sem_post()//相當(dāng)于V操作规肴,它將信號(hào)量的值加一同時(shí)發(fā)出信號(hào)來(lái)喚醒等待的進(jìn)程捶闸。
sem_getvalue()//用于得到信號(hào)量的值。
sem_destroy()//用于刪除信號(hào)量拖刃。
- 信號(hào)量代碼實(shí)戰(zhàn)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>//調(diào)用linux線程函數(shù)必須包含此頭文件
#include<semaphore.h>
#define THREAD_NUMBER 3
#define REPEAT_NUMBER 3
#define DELAY_TIME_LEVELS 3.0
sem_t sem[THREAD_NUMBER];
void * thrd_func(void *arg)//子線程入口函數(shù)
{
int thrd_num = (int)arg; //主線程對(duì)每個(gè)線程自定義了一個(gè)編號(hào)删壮,放在參數(shù)arg中
int delay_time = 0;
int count = 0;
int thrd_num1;//下個(gè)可以獲取job執(zhí)行資源的線程
printf("Thread %d is starting\n", thrd_num);
for (count = 0; count < REPEAT_NUMBER; count++)
{
//rand()為隨機(jī)數(shù)產(chǎn)生函數(shù),其隨機(jī)數(shù)的最大值為RAND_MAX(系統(tǒng)定義)兑牡,
//(int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX))將會(huì)得到一個(gè)0——9之間的隨機(jī)數(shù),注意//強(qiáng)制類型轉(zhuǎn)換int不能恃氲!发绢!
sem_wait(&sem[thrd_num]);//等待job可執(zhí)行資源
delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);//睡眠
printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);
if(thrd_num==2)
{
thrd_num1=0;
sem_post(&sem[thrd_num1]);//釋放下個(gè)線程job可執(zhí)行資源
}
else
{
thrd_num1=thrd_num+1;
sem_post(&sem[thrd_num1]);//釋放下個(gè)線程job可執(zhí)行資源
}
}
printf("Thread %d finished\n", thrd_num);
pthread_exit(NULL);//線程退出硬耍,這里也可以不要此語(yǔ)句,讓線程“自然死亡”边酒!
}
int main(void)//主線程入口
{
pthread_t thread[THREAD_NUMBER];//聲明此數(shù)組用來(lái)保存子線程號(hào)
int no = 0, res;
void * thrd_ret;
srand(time(NULL));//用當(dāng)前時(shí)間作為一個(gè)隨機(jī)數(shù)種子经柴,否則隨機(jī)數(shù)序列每次都一樣!
for (no = 0; no < THREAD_NUMBER; no++)
{
sem_init(&sem[no],0,0);//為每個(gè)線程創(chuàng)建一個(gè)信號(hào)量墩朦,并初始值為零坯认,即使三個(gè)線程初始任務(wù)可執(zhí)行資源都為0
//no的值將傳遞給線程入口函數(shù)
res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);//創(chuàng)建子線程
// sleep(1);
if (res != 0)
{
printf("Create thread %d failed\n", no);
exit(res);
}
else
{
printf("Create thread %d success\n", no);
}
}
printf("Create treads success\n Waiting for threads to finish...\n");
sem_init(&sem[0],0,1);//使線程0的信號(hào)量為1,即有資源氓涣,使其先開(kāi)始執(zhí)行
for (no = 0; no < THREAD_NUMBER; no++)
{
//按創(chuàng)建順序逐個(gè)等待子線程結(jié)束
res = pthread_join(thread[no], &thrd_ret);
if (!res)
{
printf("Thread %d joined\n", no);
}
else
{
printf("Thread %d join failed\n", no);
}
}
//刪除信號(hào)量
for (no = 0; no < THREAD_NUMBER; no++)
{
sem_destroy(&sem[no]);
}
return 0;
}