フォークされた子供の最大数を強制するにはどうすればよいですか?
-
22-07-2019 - |
質問
編集:より多くの応答を得るために、この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人の子供がアクティブになるようにします。これを処理する最良の方法は何ですか?いくつか試してみましたが、あまり運がありませんでした。
解決
子pidのリストを取得するsyscallはありませんが、 ps
はそれを実行できます。
-ppid
スイッチは、処理するすべての子をリストするため、 ps
によって出力される行数をカウントするだけです。
また、 ppid
が変更されない場合、 fork()
シグナルで増加し、 SIGCHLD
シグナルで減少する独自のカウンターを維持できます。フォーク処理済み。
他のヒント
最良の方法は、すべてのタスクをキューに追加し、必要な最大数のスレッドを起動し、各スレッドがキューからタスクを要求し、タスクを実行して次のスレッドを要求することです。実行するタスクがなくなったときにスレッドを終了することを忘れないでください。
フォークは高価な操作です。見た目から、本当に必要なのはマルチ処理ではなく、マルチスレッドです。スレッドは仮想アドレス空間を共有しますが、プロセスには個別の仮想アドレス空間があるため、スレッドはプロセスよりもはるかに軽いという点が異なります。
私はPHP開発者ではありませんが、Googleで簡単に検索したところ、PHPはネイティブにマルチスレッドをサポートしていないことがわかりましたが、仕事をするライブラリがあります。
とにかく、スレッドを生成する方法を理解したら、生成するスレッドの数を把握する必要があります。これを行うには、アプリケーションのボトルネックを知る必要があります。ボトルネックはCPU、メモリ、またはI / Oですか?コメントで、ネットワークにバインドされていることを示しました。ネットワークはI / Oの一種です。
CPUに縛られていた場合、CPUコアがあるのと同じくらいの並列性しか得られません。これ以上スレッドを追加すると、コンテキストの切り替えに時間を浪費することになります。生成する合計スレッド数を把握できると仮定すると、作業をその数のユニットに分割し、各スレッドに1つのユニットを個別に処理させる必要があります。
メモリが制限されている場合、マルチスレッドは役に立ちません。
I / Oにバインドされているため、生成するスレッドの数を把握するのは少し難しいです。すべての作業項目が非常に低い分散で処理にほぼ同じ時間を要する場合、1つの作業項目にかかる時間を測定することにより、生成するスレッドの数を推定できます。ただし、ネットワークパケットのレイテンシは非常に変動する傾向があるため、このようなことはほとんどありません。
1つのオプションは、スレッドプールを使用することです。スレッドの束全体を作成し、処理するアイテムごとに、プールに空きスレッドがあるかどうかを確認します。ある場合は、そのスレッドに作業を実行させ、次の項目に進みます。それ以外の場合は、スレッドが利用可能になるのを待ちます。スレッドプールのサイズを選択することは重要です。大きすぎるため、不必要なコンテキストの切り替えに時間を浪費しています。少なすぎるため、スレッドを頻繁に待機しています。
さらに別のオプションは、マルチスレッド/マルチプロセッシングを放棄し、代わりに非同期I / Oを実行することです。あなたがシングルコアプロセッサで作業していると言ったので、これはおそらく最速のオプションでしょう。 socket_select()
<のような関数を使用できます。 / a>ソケットに使用可能なデータがあるかどうかをテストします。存在する場合は、データを読み取ることができます。そうでない場合は、別のソケットに移動します。これにはさらに多くのブックキーピングが必要ですが、データが別のソケットで利用可能な場合、データが1つのソケットに届くのを待つことは避けてください。
スレッドと非同期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);
}
man 2 setrlimit
それはユーザーごとになりますが、とにかく 欲しいものがあります。