【Swoole】多進程process

多進程初試

用Swoole內置的函數寫多進程是一件非常苦逼的事情啊,進程間的通信,異步回調(callback)做的都不是很好。還好有Swoole,拯救了PHP在這方面的薄弱,真是讓PHP如虎添翼啊。

Swoole,適合中高級程序員,不適合初級程序員,編寫者也是衝着這麼一個思想來的,他們認爲你已經有了調試和看懂原理的能力了,對於我這種小白來說,學習還是非常吃力的。

直接來代碼吧

<?php
/**
* swoole_process只能用在cli模式中
* new swoole_process()
* 參數1 mixed $funtion 子進程創建成功後執行的函數
* 參數2 $redirect stdin stdout 重定向子進程的標準輸入和標準輸出。啓用此項後,在進程內echo將不是打印到屏幕,而是寫入到管道,讀取鍵盤輸入將會變成從管道讀取.默認爲阻塞讀取.
* $create_pipe 是否創建管道.啓用
* $redirect_stdin_stdout後,此選項將忽略用戶參數,強制爲true,如果子進程內沒有進程間通信,可以設置爲false.
*/

$test = "test";

//創建進程後的callback
function doProcess(swoole_process $worker){
    global $test;
    var_dump($test);    
    var_dump($worker);
    echo "PID ".$worker->pid.PHP_EOL;
    sleep(10);
}

//通過匿名函數創建進程
$process = new swoole_process(function(swoole_process $worker) use($test){
    echo "我是匿名函數創建的".PHP_EOL;
    var_dump($test);

});
$pid = $process->start();//start方法會返回創建的子進程的pid
//echo "pid :".$pid.PHP_EOL;

//傳入callback創建進程
$process = new swoole_process("doProcess");
$pid = $process->start();//start方法會返回創建的子進程的pid
//等待結束 不等待結束的話,自己也會結束,但是容易出現殭屍進程. 這裏會阻塞,直到所有進程執行完畢
swoole_process::wait();//通過設置參數true或false可以設置是否阻塞等待,默認爲阻塞
//等創建的倆進程全結束了纔會繼續往下執行,上面創建的兩個進程是異步的,即可以認爲是同時進行的
echo "結束了".PHP_EOL;

運行結果爲

我是匿名函數創建的
string(4) "test"
string(4) "test"
object(swoole_process)#3 (6) {//worker裏面存了管道,callback,消息隊列,pid等內容
  ["pipe"]=>
  int(6)
  ["callback"]=>
  string(9) "doProcess"
  ["msgQueueId"]=>
  NULL
  ["msgQueueKey"]=>
  NULL
  ["pid"]=>
  int(23486)
  ["id"]=>
  NULL
}
PID 23486
結束了

管道通信的例子

模擬多進程,請求多個URL的地址。此例子可以擴展爲異步多進程讀取多個文件,上傳多個文件等。

<?php
/**
* swoole_event_add()
* 參數1:int $sock
* int 文件描述符
* mixed $read callback 就是stream_socket_client/fsockopen創建的資源
* sockets資源,就是sockets擴展中socket_create創建的資源,需要在編譯時加入./configure --enable-sockets
* 參數2: 可讀回調函數
*/
echo "startTime: ".date("H:i:s").PHP_EOL;
$workers = [];//進程池
$worker_num = 3;//創建進程的數量
$url = [ 
    "www.baidu.com",
    "www.360.cn",
    "blog.diligentyang.com",
];
$result = []; 

for($i=0; $i<$worker_num; $i++){
    $process = new swoole_process('doProcess');//創建新進程
    $pid = $process->start();//啓動進程,返回pid
    $process->write($i);//向管道內寫入當前i的值
    $workers[$pid] = $process;//放入進程池中
}

//創建進程執行函數
function doProcess(swoole_process $worker){
    global $url;//獲取全局的URL,因爲是共享內存的
    $i = $worker->read();//讀取i值
    echo $url[$i].PHP_EOL;
    $res = curlTest($url[$i], (10-$i*2));//執行curl操作   
    $worker->write("pid :". $worker->pid.' res: '.$res.PHP_EOL);//往管道里面寫數據 pipe 將curl的結果再傳回主進程
    echo "寫入信息: pid: ".$worker->pid.' res: '.$res.' './*$worker->callback.*/PHP_EOL;
    //sleep(5);
    //$worker->exit(0); 退出子進程
}
//echo "sleep 5".PHP_EOL;
//添加進程事件,像每個子進程添加需要執行的動作
foreach($workers as $process){
    //添加
    //swoole_event_add($process->pipe, function($pipe) use($process){//加到事件中變爲異步模式
    //  $data = $process->read();//讀取管道數據
    //  echo "接收到: ".$data.PHP_EOL;
    //  $result[] = $data;
    //});

    $data = $process->read();//讀取管道數據  這裏是同步阻塞讀取
    echo "接收到: ".$data.PHP_EOL;
    $result[] = $data;
}

function curlTest($url, $stime){//假裝執行curl操作
    sleep($stime);//sleep一定時間,此處爲10s,8s,6s
    return "handle ". $url ." finished";
}

for($i = 0; $i < $worker_num; $i++)
{
    $ret = swoole_process::wait();
    $pid = $ret['pid'];
    unset($workers[$pid]);
    echo "Worker Exit, PID=".$pid.PHP_EOL;
}

var_dump($result);

echo "結束".PHP_EOL;
echo "startTime: ".date("H:i:s").PHP_EOL;

執行結果爲

startTime: 14:36:29
www.baidu.com
www.360.cn
blog.diligentyang.com
寫入信息: pid: 23962 res: handle blog.diligentyang.com finished 
寫入信息: pid: 23961 res: handle www.360.cn finished 
寫入信息: pid: 23960 res: handle www.baidu.com finished 
接收到: pid :23960 res: handle www.baidu.com finished

接收到: pid :23961 res: handle www.360.cn finished

接收到: pid :23962 res: handle blog.diligentyang.com finished

Worker Exit, PID=23961
Worker Exit, PID=23962
Worker Exit, PID=23960
array(3) {
  [0]=>
  string(46) "pid :23960 res: handle www.baidu.com finished
"
  [1]=>
  string(43) "pid :23961 res: handle www.360.cn finished
"
  [2]=>
  string(54) "pid :23962 res: handle blog.diligentyang.com finished
"
}
結束
startTime: 14:36:39

可以看出,多進程模式還是省了大量時間的,實際上這幾個要是順序阻塞運行,會消耗24s+的時間,而多進程只需要耗時最長的那一個時間,即10s就可以完成。

消息隊列的例子

<?php
<?php
$workers = [];//進程倉庫
$worker_num = 2;//最大進程數

for($i=0; $i<$worker_num; $i++){
    //第三個參數改爲false,才能實現進程通訊
    $process = new swoole_process('doProcess', false, false);//創建子進程
    $process->useQueue();//開啓隊列,類似於全局函數
    $pid = $process->start();
    $workers[$pid] = $process;
}

//進程執行函數
function doProcess(swoole_process $worker){
    $recv = $worker->pop();//默認是8192個長度
    echo "從主進程獲取到的數據: ". $recv."---true pid".$worker->pid.PHP_EOL;
    sleep(5);
    $worker->exit(0);
}

//主進程 向子進程添加
foreach($workers as $pid => $process){
    $process->push("hello 子進程 $pid");
}

//等待子進程結束回收資源
for($i=0; $i<$worker_num; $i++){
    $ret = swoole_process::wait();//等待執行完成
    $pid = $ret['pid'];
    unset($workers[$pid]);
    echo "子進程退出 $pid".PHP_EOL;
}

echo "this is the end".PHP_EOL;

運行結果爲

從主進程獲取到的數據: hello 子進程 24287---true pid24288
從主進程獲取到的數據: hello 子進程 24288---true pid24287
子進程退出 24287
子進程退出 24288
this is the end

可以看出兩個進程pop的順序還是未知的。

如果有不懂的地方,可以參考官網文檔,結合例子。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章