1. 程式人生 > >PHP多進程初探 --- 利用多進程開發點兒東西吧

PHP多進程初探 --- 利用多進程開發點兒東西吧

erro 思考 mas 移除 sig 失敗 function 一次 子進程

[原文地址:https://blog.ti-node.com/blog...]

幹巴巴地叨逼叨了這麽久,時候表演真正的技術了!

做個高端點兒的玩意吧,加入我們要做一個任務系統,這個系統可以在後臺幫我們完成一大波(註意是一大波)數據的處理,那麽我們自然想到,多開幾個進程分開處理這些數據,同時我們不能執行了php task.php後終端掛起,萬一一不小心關閉了終端都會導致任務失敗,所以我們還要實現程序的daemon化。好啦,開始了!

首先,我們第一步就得將程序daemon化了!

    // 設置umask為0,這樣,當前進程創建的文件權限則為777
    umask( 0 );
    $pid = pcntl_fork();
    if( $pid < 0 ){
      exit('fork error.');
    } else if( $pid > 0 ) {
      // 主進程退出
      exit();
    }
    // 子進程繼續執行
    
    // 最關鍵的一步來了,執行setsid函數!
    if( !posix_setsid() ){
      exit('setsid error.');
    }
    
    // 理論上一次fork就可以了
    // 但是,二次fork,這裏的歷史淵源是這樣的:在基於system V的系統中,通過再次fork,父進程退出,子進程繼續
    // 保證形成的daemon進程絕對不會成為會話首進程,不會擁有控制終端。
    $pid = pcntl_fork();
    if( $pid  < 0 ){
      exit('fork error');
    } else if( $pid > 0 ) {
      // 主進程退出
      exit;
    }
    // 子進程繼續執行
    // 給進程重新起個名字
    cli_set_process_title('php master process');
    

加入我們fork出5個子進程就可以搞定這些任務,那麽fork出5個子進程,同時父進程要負責這5個子進程的狀態等。

// 由於*NIX好像並沒有(如果有,請告知)可以獲取父進程fork出所有的子進程的ID們的功能,所以這個需要我們自己來保存
$child_pid = [];

// 父進程安裝SIGCHLD信號處理器並分發
pcntl_signal( SIGCHLD, function(){
  // 這裏註意要使用global將child_pid全局化,不然讀到去數組將為空,具體原因可以自己思考下
  global $child_pid;
  // 如果子進程的數量大於0,也就說如果還有子進程存活未 退出,那麽執行回收
  $child_pid_num = count( $child_pid );
  if( $child_pid_num > 0 ){
    // 循環子進程數組
    foreach( $child_pid as $pid_key => $pid_item ){
      $wait_result = pcntl_waitpid( $pid_item, $status, WNOHANG );
      // 如果子進程被成功回收了,那麽一定要將其進程ID從child_pid中移除掉
      if( $wait_result == $pid_item || -1 == $wait_result ){
        unset( $child_pid[ $pid_key ] );
      }
    }
  }
} );

// fork出5個子進程出來,並給每個子進程重命名
for( $i = 1; $i <= 5; $i++ ){
  $_pid = pcntl_fork();
  if( $_pid < 0 ){
    exit();
  } else if( 0 == $_pid ) {
    // 重命名子進程
    cli_set_process_title('php worker process');
    
    // 啦啦啦啦啦啦啦啦啦啦,請在此處編寫你的業務代碼
    // do something ...
    // 啦啦啦啦啦啦啦啦啦啦,請在此處編寫你的業務代碼
    
    // 子進程退出執行,一定要exit,不然就不會fork出5個而是多於5個任務進程了
    exit();
    
  } else if( $_pid > 0 ) {
    // 將fork出的任務進程的進程ID保存到數組中
    $child_pid[] = $_pid;
  }
}

// 主進程繼續循環不斷派遣信號
while( true ){
  pcntl_signal_dispatch();
  // 每派遣一次休眠一秒鐘
  sleep( 1 );
}

[原文地址:https://blog.ti-node.com/blog...]

PHP多進程初探 --- 利用多進程開發點兒東西吧