PHPの中の(偽)マルチスレッドとマルチプロセスを深く解析する

12186 ワード

(偽)マルチスレッド:外力を利用してWEBサーバ自体のマルチスレッドを処理し、WEBサーバからマルチスレッドを実現するプログラムを複数回呼び出す.QUOTE:PHP自体はマルチスレッドをサポートしていないことは知っていますが、WEBサーバはマルチスレッドをサポートしています.つまり複数の人が同時に訪問できるということです.これも私がPHPでマルチスレッドを実現する基礎です.私たちが今実行しているのはa.phpというファイルです.しかし、プログラムでWEBサーバに別のb.phpを実行するように要求すると、この2つのファイルは同時に実行されます.(PS:あるリンク要求が送信されると、WEBサーバはクライアントが終了するかどうかにかかわらず、それを実行する)別のファイルではなく、本ファイルの一部のコードを実行したい場合がある.どうしようかな?実はパラメータによってa.phpを制御してどのプログラムを実行します.次に例を示します.
 
  
function runThread(){
    $fp = fsockopen('localhost', 80, $errno, $errmsg);
    fputs($fp, "GET /a.php?act=brnrn");// HTTP , RFC                              
    fclose($fp);
}
function a(){
    $fp = fopen('result_a.log', 'w');
    fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
    fclose($fp);        
}
function b(){
    $fp = fopen('result_b.log', 'w');
    fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
    fclose($fp);        
}
if(!isset($_GET['act'])){ $_GET['act'] = 'a';};
if($_GET['act'] == 'a'){
    runThread();
    a();
}else if($_GET['act'] == 'b'){
    b();
};
?>

result_を開くa.logとresult_b.logは2つのファイルにアクセスする時間を比較する.この2つは確かに異なるスレッドで実行されていることがわかります.時間が全然同じです.
上はただの簡単な例で、他の形式に改善することができます.
PHPにもマルチスレッドができるようになった以上、問題も来た.それは同期の問題だ.PHP自体がマルチスレッドをサポートしていないことを知っています.だからもっと似てない
Javaのsynchronizeの方法です.どうすればいいのでしょうか?
1.できるだけ同じリソースにアクセスしない.衝突を避けるためにしかし、同時にデータベースのように操作することができる.データベースは同時操作をサポートするためである.したがって、マルチスレッドのPHPにおいて同じファイルにデータを書き込むことはない.書かなければならない場合は、別の方法で同期します.flockを呼び出すファイルをロックするなど.あるいは、一時ファイルを確立し、別のスレッドでファイルの消失を待つwhile(file_exits('xxx');このように、この一時ファイルが存在する場合は、実際にスレッドが動作していることを示す、このファイルがなければ、他のスレッドがこれを解放することを示す.
2.runThreadがfputsを実行する後にこのsocketからデータを読み取るのは極力避ける.マルチスレッドを実現するためには、非ブロックモードが必要である.すなわち、fgetsのような関数が直ちに返される.データの読み書きに問題が発生しますブロックモードを使用すると、プログラムはマルチスレッドではない.彼は上の戻りを待ってから次のプログラムを実行します.したがって、データの交換が必要な場合は、最後に外部ファイルまたはデータで完了する.本当に欲しいならsocket_set_nonblock($fp)で実現する.こんなにたくさん言ったのに、これは実際の意味があるのだろうか.いつこのような方法が必要ですか?答えは肯定的だ.みんな知ってるネットワーク資源を絶えず読み取る応用において、ネットワークの速度はボトルネックである.このような形式を多く採れば、同時に複数のスレッドで異なるページを読み取ることができる.私が作った8848、soasoなどのショッピングモールのウェブサイトから情報を検索できるプログラムです.アリババのウェブサイトからビジネス情報や会社のカタログを読み取るプログラムも使われています.この2つのプログラムは、リンクされたサーバが情報を読み取り、データベースに保存するためです.このテクノロジーを使用すると、応答を待つボトルネックが解消されます.
マルチプロセス:PHPのProcess Control Functions(PCNTL/スレッド制御関数)はUnix Like OSでのみ使用でき、Windowsでは使用できません.phpをコンパイルする場合は、--enable-pcntlを追加する必要があります.また、CLIモードでのみ実行することをお勧めします.WEBサーバ環境では実行しないでください.以下は短いテストコードです.
 
  
declare(ticks=1);
$bWaitFlag = FALSE; ///
$intNum = 10;           ///
$pids = array();        ///  PID
echo ("Start
");
for($i = 0; $i < $intNum; $i++) {
  $pids[$i] = pcntl_fork();/// , ,
  if(!$pids[$i]) {
    // _Start
    $str="";
    sleep(5+$i);
    for ($j=0;$j    echo "$i -> " . time() . " $str
";
    exit();
    // _End
  }
}
if ($bWaitFlag)
{
  for($i = 0; $i < $intNum; $i++) {
    pcntl_waitpid($pids[$i], $status, WUNTRACED);
    echo "wait $i -> " . time() . "
";
  }
}
echo ("End
");

実行結果は次のとおりです.CODE:[Copy toclipboard][qiao@oicq qiao]$ phptest.php       
Start
End
[qiao@oicq qiao]$ ps -aux | grep "php"
qiao     32275  0.0  0.5 49668 6148pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32276  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32277  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32278  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32279  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32280  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32281  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32282  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32283  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32284  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32286  0.0  0.0  1620  600pts/1    S    14:03   0:00 grep php
[qiao@oicq qiao]$ 0 -> 1133503401 
1 -> 1133503402 *
2 -> 1133503403 **
3 -> 1133503404 ***
4 -> 1133503405 ****
5 -> 1133503406 *****
6 -> 1133503407 ******
7 -> 1133503408 *******
8 -> 1133503409 ********
9 -> 1133503410 *********
[qiao@oicq qiao]$
$bWaitFlag=TUREの場合、CODE:[Copy toclipboard][qiao@oicq qiao]$ phptest.php       
Start
0 -> 1133503602 
wait 0 -> 1133503602
1 -> 1133503603 *
wait 1 -> 1133503603
2 -> 1133503604 **
wait 2 -> 1133503604
3 -> 1133503605 ***
wait 3 -> 1133503605
4 -> 1133503606 ****
wait 4 -> 1133503606
5 -> 1133503607 *****
wait 5 -> 1133503607
6 -> 1133503608 ******
wait 6 -> 1133503608
7 -> 1133503609 *******
wait 7 -> 1133503609
8 -> 1133503610 ********
wait 8 -> 1133503610
9 -> 1133503611 *********
wait 9 -> 1133503611
End
[qiao@oicq qiao]$
マルチプロセスの例から、pcntl_を使用することがわかります.fork()の後、サブプロセスが生成され、サブプロセスが実行するコードはpcntl_から生成されます.fork()以降のコードが開始し、サブプロセスは親プロセスのデータ情報を継承しない(実際には親プロセスのデータを新しいコピーした)ため、if(!$pids[$i])を使用してサブプロセスが実際に実行するコードセグメントを制御します.
より詳細な研究は時間の関係で、しばらく行われていません.私が提供したマニュアルのリンクを参考にすることができます.
[文章2]phpコマンドラインスクリプトのマルチプロセス同時実行を試みるfork,cliの下の同時実行のほかに、私の例を見てください.phpはマルチスレッドをサポートしていませんが、問題を「マルチプロセス」に変換して解決することができます.phpのpcntl_forkはunixプラットフォームのみで使用できるので,本稿ではpopenを用いて代替しようと試みた.次の例は、パラレルに呼び出されたサブルーチンコードです.
 
  
if($argc==1){
    echo("argv
");
}
$arg = $argv[1];
for($i=0; $i<10; $i++)
{
    echo($i.".1.".time()." exec $arg
");
    if($arg=='php2'){
        sleep(1);
        echo($i.".2.".time()." exec $arg
");
        sleep(1);
    }else{
        sleep(1);
    }
}
?>

プライマリ呼び出しプログラム、サブプロセスを呼び出すと同時に、サブプログラムの出力を収集します.
 
  
error_reporting(E_ALL);
$handle1 = popen('php sub.php php1', 'r');
$handle2 = popen('php sub.php php2', 'r');
$handle3 = popen('php sub.php php3', 'r');
echo "'$handle1'; " . gettype($handle1) . "
";
echo "'$handle2'; " . gettype($handle2) . "
";
echo "'$handle3'; " . gettype($handle3) . "
";
//sleep(20);
while(!feof($handle1) || !feof($handle2) || !feof($handle3) )
{
$read = fgets($handle1);
echo $read;
$read = fgets($handle2);
echo $read;
$read = fgets($handle3);
echo $read;
}
pclose($handle1);
pclose($handle2);
pclose($handle3);

次は私のマシンの出力です.
C:\my_hunter>php exec.php
'Resource id #4'; resource
'Resource id #5'; resource
'Resource id #6'; resource
0.1.1147935331 exec php1
0.1.1147935331 exec php2
0.1.1147935331 exec php3
1.1.1147935332 exec php1
0.2.1147935332 exec php2
1.1.1147935332 exec php3
2.1.1147935333 exec php1
1.1.1147935333 exec php2
2.1.1147935333 exec php3
3.1.1147935334 exec php1
1.2.1147935334 exec php2
3.1.1147935334 exec php3
4.1.1147935335 exec php1
2.1.1147935335 exec php2
4.1.1147935335 exec php3
5.1.1147935336 exec php1
2.2.1147935336 exec php2
5.1.1147935336 exec php3
6.1.1147935337 exec php1
3.1.1147935337 exec php2
6.1.1147935337 exec php3
7.1.1147935338 exec php1
3.2.1147935338 exec php2
7.1.1147935338 exec php3
8.1.1147935339 exec php1
4.1.1147935339 exec php2
8.1.1147935339 exec php3
9.1.1147935340 exec php1
4.2.1147935340 exec php2
9.1.1147935340 exec php3
5.1.1147935341 exec php2
5.2.1147935342 exec php2
6.1.1147935343 exec php2
6.2.1147935344 exec php2
7.1.1147935345 exec php2
7.2.1147935346 exec php2
8.1.1147935347 exec php2
8.2.1147935348 exec php2
9.1.1147935349 exec php2
9.2.1147935350 exec php2
**まとめ:****メインプログラムはサブプロセスをループして待ち、fgetsまたはfreadによってサブプロセスの出力を取得し、タイムスタンプから見ると、確かに同時実行を実現しています.**
-----------------------------------------------
以降の改良:*popenが開いているハンドルは一方向であり、サブプロセスへのインタラクションが必要な場合はproc_を使用します.open
*while(!feof($handle 1)|!の代わりに配列とサブ関数を使用します.feof($handle2) || !Feof($handle 3))という汚い書き方
*各ローではなく、サブプロセスで生成された出力をfreadで一度に取得します.
shellタスクを同時に実行するスケジューラです.このプログラムはタスクファイルを読み込み、中の各行のコマンドを同時に実行し、同時に存在するサブプロセスの数を設定することができます.
 
  
/*
  
  
*/
include("../common/conf.php");
include("../common/function.php");
//
$exec_number = 40 ;
/***** main ********/
if($argc==1){
    echo("argv
");
}
$taskfile = $argv[1];
//tasklist
$tasklist = file($taskfile);
$tasklist_len = count($tasklist);
$tasklist_pos = 0;
$handle_list = array();
while(1)
{
    // ,
    if($exec_number > count($handle_list) &&
            $tasklist_pos < $tasklist_len)
    {
        for($i=$tasklist_pos; $i        {
            $command = $tasklist[$i] ;
            $handle_list[] = popen($command , "r" );
            tolog("begin task \t ".$tasklist[$i]);
            $i++;
            if($exec_number == count($handle_list)) break;
        }
        $tasklist_pos = $i;
    }
    // ,
    if(0 == count($handle_list))
    {
        break;
    }
    // ,
    $end_handle_keys = array();
    foreach($handle_list as $key => $handle)
    {
        //$str = fgets($handle, 65536);
        $str = fread($handle, 65536);
        echo($str);
        if(feof($handle))
        {
            $end_handle_keys[] = $key;
            pclose($handle);
        }
    }
    //
    foreach($end_handle_keys as $key)
    {
        unset($handle_list[$key]);
        //var_dump($handle_list);
        //exit;
    }
}
tolog("

*******************end**********************

", "" ,  true);

Socketマルチプロセスが受信したコードを追加します.
 
  
do {
 if (($msgsock = socket_accept($sock)) < 0) {
  echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "
";
  break;
 }
 $pid = pcntl_fork();
 if ($pid == -1) {
  die('could not fork');
 } else if (!$pid) {
  .....
  socket_write($msgsock, $msg, strlen($msg));
  do {
   ......
  } while (true);
   socket_close($msgsock);
 }
} while (true);