(推薦教學:PHP影片教學)
所謂臨界區(也稱為臨界區段)就是存取和操作共享資料的程式碼段。
進程互斥:兩個或以上的進程不能同時進入關於同一組共享變數的臨界區域,即一個進程正在存取臨界資源,另一個進程要想存取必須等待。
進程同步:主要研究如何確定數個進程之間的執行順序和避免資料競爭的問題即,如何讓多個進程能一塊很好的協作運行
所謂同步,就是並發進程/執行緒在一些關鍵點上可能需要互相等待與互通訊息,這種相互制約的等待與互通訊息稱為進程/執行緒同步。
舉個生活的同步例子,你肚子餓了想要吃飯,你叫媽媽早點做飯,媽媽聽到後就開始做飯,但是在媽媽沒有做完飯之前,你必須阻塞等待,等媽媽做完飯後,自然會通知你,接著你吃飯的事情就可以進行了。
注意,同步與互斥是兩種不同的概念:
同步就好比:「操作A 應在操作B 之前執行」,「操作C 必須在操作A 與操作B都完成之後才能執行」等;
互斥就好比:「操作A 和操作B 不能在同一時刻執行」;
信號量用途:主要用於多進程或多執行緒對公共資源物件的存取控制。用來解決多進程(多執行緒同步的問題),類似一把鎖,在存取前取得鎖(取得不到則等待),存取後釋放鎖。
多進程/多執行緒一般是並發執行,如果對公共資源存取沒有做同步處理,很容易造成資料破壞
信號量其實是一個整數的計數器,主要用於實現進程間的互斥與同步,而不是用於快取進程間通訊的資料。
信號量表示資源的數量,控制信號量的方式有兩種原子操作:
一個是P 操作,這個操作會把信號量減去-1,相減後如果信號量a99d80cca695d2f5d8f7729cbf7655ca= 0,則表示還有資源可使用,進程可正常繼續執行。
另一個是V 操作,這個操作會把信號量加上1,相加後如果信號量c3871aa0c533f9a0909a9e173d2e13d4 0,則表示當前沒有阻塞中的進程;
P 操作是用在進入共享資源之前,V 操作是用在離開共享資源之後,這兩個操作是必須成對出現的。
舉個類比,2 個資源的信號量,相當於2 條火車軌道,PV 操作如下圖過程:
一輛火車進入軌道,相當於信號量的P操作,資源-1,這樣就剩下一條軌道
#接著又一輛火車佔用另一條軌道,也就是P操作,資源-1
此時交通號誌燈變為紅色,因為沒有軌道可用,第三輛火車必須等待
第一輛火車離開軌道,相當於V操作,此時軌道資源為1,交通燈變為綠燈
#第三輛火車發現交通號誌變綠,於是進入火車軌道,軌道資源耗盡為0,於是交通號誌燈變成紅燈
在這個火車軌道系統中,軌道是公共資源,每輛火車好比一個線程,交通號誌的就是號誌量的作用。信號量可以實現鎖的互斥操作,也可以實現進程/線程同步
1)二進位信號量(也叫二值信號量)
此時訊號量的初值只能是0和1。 (二進位信號量可以實現互斥鎖操作)
2)一般/計數信號量
此時信號量的初值可以是任意非負數。顯然,其包含二進位信號量。上面舉的火車軌道例子就可以使用計數信號量來實現,一般計數信號量與鎖的區別是它可以允許多個線程/進程(線程的數量由計數信號量初值定義) 同時操作公共資源
一般只有在開發多進程的時候才可能遇到需要使用信號量的場景,phper 幾乎很少有使用信號量的場景,就算有多進程對公共資源操作,大多也是使用flock 文件鎖做互斥操作
<?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 $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影片教學)
以上是簡單了解php進程通訊之信號量的詳細內容。更多資訊請關注PHP中文網其他相關文章!