線程同步與互斥

臨界區(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;


      信號(hào)量的互斥

      信號(hào)量的同步
  • 信號(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;        
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牛哺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子劳吠,更是在濱河造成了極大的恐慌引润,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痒玩,死亡現(xiàn)場(chǎng)離奇詭異淳附,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蠢古,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門奴曙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人草讶,你說(shuō)我怎么就攤上這事洽糟。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵坤溃,是天一觀的道長(zhǎng)拍霜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)薪介,這世上最難降的妖魔是什么沉御? 我笑而不...
    開(kāi)封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮昭灵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伐谈。我一直安慰自己烂完,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布诵棵。 她就那樣靜靜地躺著抠蚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪履澳。 梳的紋絲不亂的頭發(fā)上嘶窄,一...
    開(kāi)封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音距贷,去河邊找鬼柄冲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛忠蝗,可吹牛的內(nèi)容都是我干的现横。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼阁最,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼戒祠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起速种,我...
    開(kāi)封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤姜盈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后配阵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體馏颂,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年闸餐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饱亮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舍沙,死狀恐怖近上,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拂铡,我是刑警寧澤壹无,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布葱绒,位于F島的核電站,受9級(jí)特大地震影響斗锭,放射性物質(zhì)發(fā)生泄漏地淀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一岖是、第九天 我趴在偏房一處隱蔽的房頂上張望帮毁。 院中可真熱鬧,春花似錦豺撑、人聲如沸烈疚。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爷肝。三九已至,卻和暖如春陆错,著一層夾襖步出監(jiān)牢的瞬間灯抛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工音瓷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留对嚼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓绳慎,卻偏偏與公主長(zhǎng)得像猪半,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偷线,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 多線程三個(gè)特征:原子性、可見(jiàn)性以及有序性. 同步鎖 /并發(fā)鎖/ 讀寫鎖亥曹,顯示鎖邓了, ReentrantLock與Co...
    架構(gòu)師springboot閱讀 1,915評(píng)論 0 5
  • 1、多線程并行和并發(fā)的區(qū)別 并行:多個(gè)處理器或者多核處理器同時(shí)執(zhí)行多個(gè)不同的任務(wù)媳瞪。 并發(fā):一個(gè)處理器處理多個(gè)任務(wù)骗炉。...
    北山學(xué)者閱讀 5,341評(píng)論 0 5
  • Linux--線程編程 多線程編程-互斥鎖 線程同步與互斥 互斥鎖 信號(hào)量 條件變量 互斥鎖 互斥鎖的基本使用...
    吃蘋果的貓C閱讀 624評(píng)論 1 6
  • 01概述 線程同步,是協(xié)調(diào)步調(diào)蛇受,按預(yù)定先后次序執(zhí)行句葵,解決與時(shí)間相關(guān)的錯(cuò)誤。 線程不同步,產(chǎn)生的現(xiàn)象就是數(shù)據(jù)混亂: ...
    程序員姜戈閱讀 97評(píng)論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月乍丈,有人笑有人哭剂碴,有人歡樂(lè)有人憂愁,有人驚喜有人失落轻专,有的覺(jué)得收獲滿滿有...
    陌忘宇閱讀 8,531評(píng)論 28 53