3

編集:より多くの応答を得ることを期待して、この C にタグを付けました。特定の言語の実装よりも、私が興味を持っているのは理論です。したがって、C コーダーである場合は、次の PHP を疑似コードとして扱い、C で記述された回答を自由に返信してください。

シリアルではなく並列でタスクを実行することにより、PHP CLI スクリプトを高速化しようとしています。タスクは互いに完全に独立しているため、開始/終了の順序は関係ありません。

元のスクリプトは次のとおりです (わかりやすくするために、これらの例はすべて省略されていることに注意してください)。

<?php

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

foreach ($items as $item) {
    do_stuff_with($item);
}

以下に示すよう$itemsに、並行して動作させることができました。pcntl_fork()

<?php

ini_set('max_execution_time', 0); 
ini_set('max_input_time', 0); 
set_time_limit(0);

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

$pids = array();
foreach ($items as $item) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork()");
    } elseif ($pid > 0) {
        // parent
        $pids[] = $pid;
    } else {
        // child
        do_stuff_with($item);
        exit(0);
    }   
}

foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

今度はこれを拡張して、たとえば一度に最大 10 人の子供がアクティブになるようにします。これを処理する最良の方法は何ですか? 私はいくつかのことを試しましたが、あまり運がありませんでした。

4

4 に答える 4

2

子 pid のリストを取得するための syscall はありませんが、それpsを行うことができます。

--ppidswitch は、処理するすべての子をリストするので、 によって出力された行数をカウントするだけで済みますps

または、フォークされた処理のために変更されていないと仮定して、シグナルでインクリメントfork()およびデクリメントする独自のカウンターを維持することもできます。SIGCHLDppid

于 2008-12-03T08:09:00.300 に答える
2

私が思いつく最善の方法は、すべてのタスクをキューに追加し、必要な最大数のスレッドを起動してから、各スレッドがキューからタスクを要求し、タスクを実行して次のタスクを要求することです。実行するタスクがなくなったら、スレッドを終了させることを忘れないでください。

于 2008-12-03T05:51:40.700 に答える
2

フォークはコストのかかる操作です。見た目からして、本当に必要なのはマルチスレッドであり、マルチプロセッシングではありません。違いは、スレッドはプロセスよりもはるかに軽量であることです。スレッドは仮想アドレス空間を共有しますが、プロセスは別の仮想アドレス空間を持っているためです。

私は PHP 開発者ではありませんが、Google で簡単に検索すると、PHP はネイティブでマルチスレッドをサポートしていないことがわかりますが、その作業を行うためのライブラリは存在します。

とにかく、スレッドを生成する方法を理解したら、生成するスレッドの数を把握する必要があります。これを行うには、アプリケーションのボトルネックが何であるかを知る必要があります。ボトルネックは、CPU、メモリ、または I/O ですか? コメントで、ネットワークにバインドされていること、およびネットワークが I/O の一種であることを示しました。

CPU バウンドの場合、CPU コアの数だけ並列処理を行うことができます。これ以上のスレッドでは、コンテキスト スイッチを行うのに時間を無駄にしているだけです。生成するスレッドの合計数を把握できると仮定すると、作業をその数のユニットに分割し、各スレッドで 1 つのユニットを個別に処理する必要があります。

メモリに縛られている場合、マルチスレッドは役に立ちません。

I/O バウンドであるため、生成するスレッドの数を把握するのは少し面倒です。すべての作業項目の処理時間がほぼ同じで分散が非常に小さい場合、1 つの作業項目にかかる時間を測定することで、生成されるスレッドの数を見積もることができます。ただし、ネットワーク パケットのレイテンシは大きく変動する傾向があるため、そのようなことはほとんどありません。

1 つのオプションは、スレッド プールを使用することです。つまり、大量のスレッドを作成してから、処理する各項目について、プールに空きスレッドがあるかどうかを確認します。ある場合は、そのスレッドに作業を実行させ、次の項目に進みます。それ以外の場合は、スレッドが使用可能になるまで待機します。スレッド プールのサイズを選択することは重要です。大きすぎると、不要なコンテキスト スイッチを実行して時間を浪費することになります。少なすぎると、スレッドを頻繁に待ちます。

さらに別のオプションは、マルチスレッド/マルチプロセッシングを放棄し、代わりに非同期 I/O を行うことです。シングルコア プロセッサで作業しているとおっしゃっていたので、これがおそらく最速のオプションになるでしょう。socket_select()ソケットに利用可能なデータがあるかどうかをテストするような関数を使用できます。存在する場合は、データを読み取ることができます。そうでない場合は、別のソケットに移動します。これには、より多くの簿記を行う必要がありますが、別のソケットでデータが使用可能になっているときに、あるソケットでデータが受信されるのを待つ必要はありません。

スレッドと非同期 I/O を避けてマルチプロセッシングに固執したい場合でも、アイテムごとの処理が十分に高価であれば、それでも価値があります。次に、次のように作業分割を行うことができます。

$my_process_index = 0;
$pids = array();

// Fork off $max_procs processes
for($i = 0; $i < $max_procs - 1; $i++)
{
  $pid = pcntl_fork();
  if($pid == -1)
  {
    die("couldn't fork()");
  }
  elseif($pid > 0)
  {
    // parent
    $my_process_index++;
    $pids[] = $pid
  }
  else
  {
    // child
    break;
  }
}

// $my_process_index is now an integer in the range [0, $max_procs), unique among all the processes
// Each process will now process 1/$max_procs of the items
for($i = $my_process_index; $i < length($items); $i += $max_procs)
{
  do_stuff_with($items[$i]);
}

if($my_process_index != 0)
{
  exit(0);
}
于 2008-12-03T05:57:54.853 に答える
0

男 2 setrlimit

それはユーザーごとになり、とにかくあなたが望むものになるかもしれません。

于 2008-12-03T05:36:53.163 に答える