簡單瞭解php程序通訊之號誌

2022-09-02 18:02:20

(推薦教學:)

常見程序通訊方式

一些理論基礎

  • 臨界資源:每次僅允許一個程序存取的資源。
  • 臨界區:每個程序中存取臨界資源的那段程式碼叫臨界區

所謂臨界區(也稱為臨界段)就是存取和操作共用資料的程式碼段。

程序互斥:兩個或以上的程序不能同時進入關於同一組共用變數的臨界區域,即一個程序正在存取臨界資源,另一個程序要想存取必須等待。

程序同步:主要研究如何確定數個程序之間的執行順序和避免資料競爭的問題 即,如何讓多個程序能一塊很好的共同作業執行

所謂同步,就是並行程序/執行緒在一些關鍵點上可能需要互相等待與互通訊息,這種相互制約的等待與互通訊息稱為程序/執行緒同步。

舉個生活的同步例子,你肚子餓了想要吃飯,你叫媽媽早點做菜,媽媽聽到後就開始做菜,但是在媽媽沒有做完飯之前,你必須阻塞等待,等媽媽做完飯後,自然會通知你,接著你吃飯的事情就可以進行了。

注意,同步與互斥是兩種不同的概念:

同步就好比:「操作 A 應在操作 B 之前執行」,「操作 C 必須在操作 A 和操作 B 都完成之後才能執行」等;

互斥就好比:「操作 A 和操作 B 不能在同一時刻執行」;

system V 號誌

號誌用途:主要用於多程序或多執行緒對公共資源物件的存取控制。 用來解決多程序(多執行緒同步的問題),類似於一把鎖,存取前獲取鎖(獲取不到則等待),存取後釋放鎖。

多程序/多執行緒一般是並行執行,如果對公共資源存取沒有做同步處理,很容易造成資料破壞

號誌其實是一個整型的計數器,主要用於實現程序間的互斥與同步,而不是用於快取程序間通訊的資料。

號誌表示資源的數量,控制號誌的方式有兩種原子操作:

一個是 P 操作,這個操作會把號誌減去 -1,相減後如果號誌 < 0,則表明資源已被佔用,程序需阻塞等待;相減後如果號誌 >= 0,則表明還有資源可使用,程序可正常繼續執行。

另一個是 V 操作,這個操作會把號誌加上 1,相加後如果號誌 <= 0,則表明當前有阻塞中的程序,於是會將該程序喚醒執行;相加後如果號誌 > 0,則表明當前沒有阻塞中的程序;

P 操作是用在進入共用資源之前,V 操作是用在離開共用資源之後,這兩個操作是必須成對出現的。

舉個類比,2 個資源的號誌,相當於 2 條火車軌道,PV 操作如下圖過程:

一輛火車進入軌道,相當於號誌的P操作,資源-1,這樣就剩下一條軌道

接著又一輛火車佔用另一條軌道,也就是P操作,資源-1

此時交通訊號燈變為紅色,因為沒有軌道可用,第三輛火車必須等待

第一輛火車離開軌道,相當於V操作,此時軌道資源為1,交通燈變為綠燈

第三輛火車發現交通訊號燈變綠,於是進入火車軌道,軌道資源耗盡為0,於是交通訊號燈變為紅燈

在這個火車軌道系統中,軌道是公共資源,每輛火車好比一個執行緒,交通訊號燈起的就是號誌的作用。號誌可以實現鎖的互斥操作,也可以實現程序/執行緒同步

號誌型別

1)二進位制號誌(也叫二值號誌)

此時號誌的初值只能是0和1。(二進位制號誌可以實現互斥鎖操作)

2)一般/計數號誌

此時號誌的初值可以是任意非負數。顯然,其包含二進位制號誌。上面舉的火車軌道例子就可以使用計數號誌來實現,一般計數號誌與鎖的區別是它可以允許多個執行緒/程序(執行緒的數量由計數號誌初值定義) 同時操作公共資源

一般只有在開發多程序的時候才可能遇到需要使用號誌的場景,phper 幾乎很少有使用號誌的場景,就算有多程序對公共資源操作,大多也是使用 flock 檔案鎖做互斥操作

php模擬多程序操作公共資源

<?php
$file = "num.txt";//定一個空檔案
$count =0;
file_put_contents($file,$count);

$pid = pcntl_fork();//fork 一個程序

if($pid == 0){//子程序執行邏輯
    $x = (int)file_get_contents($file);//讀取檔案內容
    //i 迴圈累加
    for($i=0; $i<1000; $i++){
        $x = $x + 1;
    }
    //寫入檔案
    file_put_contents($file,$x);
    //子程序退出
    exit(0);
}
//父程序執行邏輯
$x = (int)file_get_contents($file);
for($i=0; $i<1000; $i++){
    $x = $x+1;
}
//累加寫入
file_put_contents($file,$x);

在編寫一個shell 指令碼輔助

#!/bin/bash
for a in {1..1000}
do
    (php demo1.php)
    b=`cat num.txt`
    if [ $b != 2000 ]
    then
        echo -e "錯誤$b"
    fi
done

按理來說,變數 $x 最後寫入檔案的值應該是2000,但很不幸,並不是如此,我們對上面的指令碼執行一下:

執行了1000次,發現出現了變數$x值結果是 1000 的有8次,雖然發生錯誤的概率比較小,但是在計算機裡是不能容忍的。

為什麼會出現這種情況,我們知道單核cpu系統裡為了實現多個程式同時執行的假象,作業系統通常都採用時間片排程,一個程序時間片用完就切換下一個程序執行,加上我們的高階語言不是每一行程式碼都是原子性的,比如x = (int)file_get_contents($file) 這行程式碼對於我們來說是不可分割是原子性的,但是經過編譯器編譯成組合碼【機器指令】可能是多條指令實現,這樣就會出現問題,如果指令只執行到一半程序分配的時間片用完或者被其他程序打斷,都有可能造成資料損壞,導致最後計算結果出現誤差

使用php封裝system v 號誌集函數

<?php
$file = "num.txt";//定一個空檔案
$count =0;
$key = ftok("demo1.php","x");
$sem_id = sem_get($key,1);// 第二個引數是個整數,表示設定號誌集,設定為1 把它當做二值號誌來用,用於互斥
file_put_contents($file,$count);
$pid = pcntl_fork();//fork 一個程序
if($pid == 0){//子程序執行邏輯
sem_acquire($sem_id); // P -1 操作 獲取一個號誌 , 如果為0表示資源被佔用程序掛起等待號誌釋放
    $x = (int)file_get_contents($file);//讀取檔案內容
    //i 迴圈累加
    for($i=0; $i<1000; $i++){
        $x = $x + 1;
    }
    //寫入檔案
    file_put_contents($file,$x);
       sem_release($sem_id); //V +1 操作 釋放號誌
    //子程序退出
    exit(0);
}
//父程序執行邏輯
sem_acquire($sem_id); // P -1 操作  獲取號誌, 如果為0表示資源被佔用程序掛起等待號誌釋放
$x = (int)file_get_contents($file);
for($i=0; $i<1000; $i++){
    $x = $x+1;
}
//累加寫入
file_put_contents($file,$x);
sem_release($sem_id); //V +1 操作 釋放號誌

加入號誌後,那就一定保證100%是2000,絕對不會出現其他數值。

(推薦教學:)

以上就是簡單瞭解php程序通訊之號誌的詳細內容,更多請關注TW511.COM其它相關文章!