進程并發(fā)編程基礎

前言:看了操作系統(tǒng)并發(fā)編程的基礎,做個筆記并用 C 實現常見的一種并發(fā)編程的模型——消費者馍佑、生產者模型

進程之間的關系

第一種是競爭關系

系統(tǒng)中的多個進程之間彼此無關,它們并不知道其他進程的存在茵臭,并且也不受其他進程執(zhí)行的影響舅世。因而, 必然要出現多個進程競爭資源的問題社证。當多個進程競爭共享硬設備评凝、存儲器奕短、處理器 和文件等資源時匀钧,操作系統(tǒng)必須協(xié)調好進程對資源的爭用

進程的互斥(mutual exclusion )是解決進程間競爭關系(間接制約關系)的手段日杈。 進程互斥指若干個進程要使用同一共享資源時佑刷,任何時刻最多允許一個進程去使用,其他要使用該資源的進程必須等待涨冀,直到占有資源的進程釋放該資源麦萤。

第二種是協(xié)作關系

某些進程為完成同一任務需要分工協(xié)作,由于合作的每一個進程都是獨立地以不可預知的速度推進翅帜,這就需要相互協(xié)作的進程在某些協(xié)調點上協(xié) 調各自的工作命满。

進程的同步(Synchronization)是解決進程間協(xié)作關系(直接制約關系)的手段。進程同步指兩個以上進程基于某個條件來協(xié)調它們的活動狭莱。一個進程的執(zhí)行依賴于另一個協(xié)作進程的消息或信號腋妙,當一個進程沒有得到來自于另一個進程的消息或信號時則需等待,直到消息或信號到達才被喚醒骤素。

不難看出,進程互斥關系是一種特殊的進程同步關系痕檬,即逐次使用互斥共享資源送浊,也是對進程使用資源次序上的一種協(xié)調袭景。

如何實現互斥

無論是競爭還是協(xié)作,都必須實現互斥耸棒。

而實現互斥的關鍵在于:進程 A 正在使用的臨界資源与殃,哪怕在使用的過程中被中斷了,其他進程的臨界區(qū)都不能動進程 A 的資源米奸。

硬件方式實現互斥

用硬件的方式增加一條匯編指令衣屏,比如 TestAndSet

偽代碼如下

// 主程序
global lock = false

    

repeate {nothing} until TestAndSet(&lock)
// 臨界區(qū)代碼
// do someting
// 臨界區(qū)結束
lock = false

這里的 TestAndSet 使用匯編指令實現的,也就是他不能被中斷膨疏。lock 是全局變量钻弄,所有的進程都能訪問到

實現的功能就是:檢查 lock 是否是 false佃却,如果是 false 就把 lock 設為 true。如果 lock 是 false窘俺。就不會執(zhí)行repeate {nothing}饲帅。

有的進程執(zhí)行的時候,lock 是 true。就會一直執(zhí)行 repeate {nothing}灶泵,直到 CPU 時間片結束育八,結束調用。這樣就實現了互斥赦邻。

但是髓棋,這樣有一個很大的缺點就是忙等,我們不想要忙等惶洲,我們想要 lock = true 的時候進程被阻塞按声,然后等待調用

用信號量(semaphore)實現互斥 重點恬吕!

semaphore 最重要的是兩個函數:

  • wait(semaphore* s)

  • signal(semaphore* s)

這是系統(tǒng)給的原語签则。原語和其他函數最大的區(qū)別就是:它雖然也是一系列指令構成,但是在執(zhí)行的過程中不可中斷

wait 和 signal 的偽代碼如下:

wait(semaphore* s) {
    if(s.count-- < 0) {
        // 阻塞
    }
}

signal(semaphore* s) {
    if(s.count++ <= 0) {
        // 調用某個被阻塞的進程
        call();
    }
}

那怎么用這兩個函數實現互斥呢铐料?我們在訪問臨界資源的時候會先執(zhí)行 wait 函數如果 (s.count-- < 0)渐裂。則被阻塞,而不是忙等余赢。否則就執(zhí)行臨界區(qū)代碼(資源夠用芯义,不需要阻塞)哈垢。

而執(zhí)行完臨界區(qū)以后妻柒,會執(zhí)行 signal 函數。如果 (s.count++ <= 0) 耘分。則去喚醒一個被阻塞的函數举塔,否則啥也不做,僅僅加一(沒有被阻塞的進程)求泰。

通過一個實例來理解這一個過程

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <unistd.h>


sem_t s;


int main(int argc, char const *argv[])
{
    int value = 0;

    sem_init(&s, 0, 10);

    sem_getvalue(&s, &value);
    printf("initialization s.count = %d\n", value);

    // wait
    sem_wait(&s);
    sem_getvalue(&s, &value);
    printf("after wait s.count = %d\n", value);

    // signal
    sem_post(&s);
    sem_getvalue(&s, &value);
    printf("after signal s.count = %d\n", value);

    
    sem_destroy(&s);

    return 0;
}

輸出:

initialization s.count = 10
after wait s.count = 9
after signal s.count = 10

C 實例

我們通過兩個經典模型央渣,來深入理解進程并發(fā)編程

消費者、生產者模型

該問題描述了共享固定大小緩沖區(qū)的兩個進程——即所謂的“生產者”和“消費者”——在實際運行時會發(fā)生的問題渴频。生產者的主要作用是生成一定量的數據放到緩沖區(qū)中芽丹,然后重復此過程。與此同時卜朗,消費者也在緩沖區(qū)消耗這些數據拔第。該問題的關鍵就是要保證生產者不會在緩沖區(qū)滿時加入數據,消費者也不會在緩沖區(qū)中空時消耗數據场钉。

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <unistd.h>

#define SIZE 1 /*buffer size*/



sem_t empty;
sem_t full;
sem_t mutex;

int buffer[SIZE] = {0};

void producer(int idx, int value) {
    sem_wait(&full);
    sem_wait(&mutex);

    buffer[idx] = value;

    sem_post(&mutex);
    sem_post(&empty);
}

void consumer(int idx) {

    sem_wait(&empty);
    sem_wait(&mutex);

    printf("從 buffer[%d] 取出 %d \n", idx, buffer[idx]);
    buffer[idx] = -1;

    sem_post(&mutex);
    sem_post(&full);
}

int main(int argc, char const *argv[]) {
    int value;


    // 多個生產者和消費者蚊俺,只有一個進程能夠擁有 buffer,這是所有進程的互斥
    sem_init(&mutex, 0, 1);


    // 生產者在滿的時候不能生產
    sem_init(&full, 0, SIZE);

    // 消費者不能在空的時候消費
    sem_init(&empty, 0, 0);

    // 假設有四個進程  
    for(int i = 0; i < 4; i++) {
        int p = fork();


        if(p == 0) {

            switch (i)
            {
                case 0:
                    producer(0, 10);
                    break;
                case 1:
                    producer(0, 100);
                    break;
                case 2:
                    consumer(0);
                    break;
                case 3:
                    consumer(0);
                    break;
                default :
                    // producer(0, 10);
                    break;
            }

        } else {
            printf("我是主進程:%d \n", p);
        }

        
    }

    sem_destroy(&mutex);
    sem_destroy(&empty);
    sem_destroy(&full);

    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末逛万,一起剝皮案震驚了整個濱河市泳猬,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖得封,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埋心,死亡現場離奇詭異,居然都是意外死亡忙上,警方通過查閱死者的電腦和手機踩窖,發(fā)現死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晨横,“玉大人洋腮,你說我怎么就攤上這事∈中危” “怎么了啥供?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長库糠。 經常有香客問我伙狐,道長,這世上最難降的妖魔是什么瞬欧? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任贷屎,我火速辦了婚禮,結果婚禮上艘虎,老公的妹妹穿的比我還像新娘唉侄。我一直安慰自己,他們只是感情好野建,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布属划。 她就那樣靜靜地躺著,像睡著了一般候生。 火紅的嫁衣襯著肌膚如雪同眯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天唯鸭,我揣著相機與錄音须蜗,去河邊找鬼。 笑死目溉,一個胖子當著我的面吹牛明肮,可吹牛的內容都是我干的。 我是一名探鬼主播停做,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼晤愧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛉腌?” 一聲冷哼從身側響起官份,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤只厘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舅巷,有當地人在樹林里發(fā)現了一具尸體羔味,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年钠右,在試婚紗的時候發(fā)現自己被綠了赋元。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡飒房,死狀恐怖搁凸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情狠毯,我是刑警寧澤护糖,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嚼松,受9級特大地震影響嫡良,放射性物質發(fā)生泄漏。R本人自食惡果不足惜献酗,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一寝受、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罕偎,春花似錦很澄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忙干。三九已至器予,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捐迫,已是汗流浹背乾翔。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留施戴,地道東北人反浓。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像赞哗,于是被迫代替她去往敵國和親雷则。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355