ホームページ >バックエンド開発 >PHPチュートリアル >PHPのPthreadsを使用した並列プログラミング - 基礎
キーポイント
PHP開発者は、並列性をめったに利用しないようです。同期のシングルスレッドプログラミングのシンプルさは非常に魅力的ですが、少し並行性を使用すると、価値のあるパフォーマンスの改善につながる場合があります。
簡単な説明:Pthreads V2はPHP 5.xを対象としており、PTHREADS V3はPHP 7.xを対象としており、アクティブな開発中です。
私の投稿を校正し、改善してくれたJoe Watkins(Pthreads拡張機能の作成者)に感謝します!
pthreadsを使用しない場合
先に進む前に、最初に
(および)を使用してはならない状況を説明したいと思います。
pthreads v2では、Webサーバー環境(つまり、FCGIプロセス)でpthreadsを使用することをお勧めしません。 Pthreads v3から始めて、この提案は実施されるため、Webサーバー環境ではまったく使用できません。これを行う主な2つの理由は、です 非同期プログラミングの方向を指摘させてください。 SitePointは、興味があれば、このトピックに関するいくつかの優れた記事(非同期ライブラリの作成やMinecraftの修正など)を公開しています。
ポイントに戻って、トピックにまっすぐに行きましょう! 1回限りのタスクのトレーニング 例:
上記の上記の実行方法は、新しいスレッドで実行する作業単位です。 Thread :: Startが呼び出されると、新しいスレッドが生成され、実行方法が呼び出されます。次に、生成されたスレッドをメインスレッド(スレッド::結合経由で)に再加入します。これは、個々のスレッドが実行を完了するまでブロックします。これにより、結果を出力しようとする前にタスクが実行を完了したことが保証されます($ task-> responseに保存)。
スレッドクラスを何らかの方法で拡張する必要があります。これは、異なるスレッドで実行するために必要な機能を提供するだけでなく、暗黙のセキュリティと有用なインターフェイス(リソース同期など)を提供するためです。
スレッドクラスとスレッドクラスの基本をすでに学んでいるので、残りの3つ(労働者、揮発性、およびプール)を見てみましょう。 各タスクが並列化されるために新しいスレッドを開始するのは費用がかかります。これは、PHP内にスレッドを実装するために、pthreadsが共有されたステートレスアーキテクチャを採用する必要があるためです。これは、PHPインタープリターの現在のインスタンスの実行コンテキスト全体(各クラス、インターフェイス、属性、および関数を含む)を、作成したスレッドごとにコピーする必要があることを意味します。これにはパフォーマンスが大きな影響を与える可能性があるため、スレッドは常に可能な限り再利用する必要があります。スレッドを再利用するには、ワーカーの使用またはプールの使用方法は2つあります。 ワーカークラスは、別のスレッドで一連のタスクを同期して実行するために使用されます。これは、新しいワーカーインスタンス(これにより新しいスレッドが作成されます)を作成し、その別のスレッドにタスクを積み重ねることによって行われます(ワーカー::スタックを介して)。 これは簡単な例です: output: 上記のスタック15のタスクは、ワーカー::スタックを介して新しい$ Workerオブジェクトにタスクを設定し、スタッキング順序で処理します。上記のように、ワーカー::収集方法は、タスクが実行を完了した後にタスクをクリーンアップするために使用されます。 whileループでそれを使用することにより、すべての積み重ねられたタスクが実行され、クリーンアップされるまでメインスレッドをブロックし、その後、ワーカー::シャットダウンをトリガーします。ワーカーを時期尚早に閉じる(つまり、タスクを実行する必要がありますが)すべてのタスクが実行されるまでメインスレッドをブロックします - タスクはガベージが収集されません(メモリリークを引き起こします)。
上記の例を調整して、ワーカープールを使用しましょう。
良い実践として、タスクを完了した後、常に労働者プログラムやプールのタスクを収集し、手動で閉じる必要があります。スレッドクラスを介して作成されたスレッドも、作成者スレッドに再会する必要があります。 導入される最後のクラスは揮発性です。これは、pthreads v3への新しい追加です。 Pthreadsの重要な概念になっているのは、それがなければ、パフォーマンスがひどく劣化するためです。したがって、デフォルトでは、スレッドオブジェクトであるスレッドクラスのプロパティは、現在は不変であるため、最初の割り当て後に再割り当てすることはできません。現在、これらの特性を明示的に変異させる傾向がありますが、新しい揮発性クラスを使用して実行できます。 新しい不変性の制約を実証するために、例をすばやく見てみましょう:
デモとして
シンプルなカウンターを実装しましょう:
正しい出力を取得するように同期を追加してこれを修正しましょう20:
以下は、2つの同期中のループからのインターリーブの増分です。
スレッド:: waitの呼び出しの周りに追加の条件が追加されていることに気付いたかもしれません。これらの条件は、通知が 虚偽のウェイクアップコールに対して脆弱になります。これにより、コードが予測不可能になります。 各クラスの使用を導入するなど、Pthreadsが付属する5つのクラス(スレッド、スレッド、ワーカー、揮発性、プール)が見られました。また、Pthreadsの不変性の新しい概念と、サポートする同期機能をすばやく調べました。これらの基本がカバーされているので、いくつかの実際のユースケースにpthreadsを適用することを検討することができます!これが次の投稿の主題になります。
php
LaravelやSymfonyなどのPHPフレームワークでpthreadsを使用できますか?
これが、この環境でスレッドが良い解決策ではない理由です。 IOブロッキングタスク(HTTPリクエストの実行など)としてスレッドのソリューションを探している場合は、AMPなどのフレームワークを通じて達成できるマルチスレッドの方法で1回限りのタスクを処理したい場合があります(IOバインドタスクの実行など)。この場合、スレッドクラスを使用して新しいスレッドを作成し、その別のスレッドでいくつかの作業単位を実行できます。
<code class="language-php">$task = new class extends Thread {
private $response;
public function run()
{
$content = file_get_contents("http://google.com");
preg_match("~<title>(.+)</title>~", $content, $matches);
$this->response = $matches[1];
}
};
$task->start() && $task->join();
var_dump($task->response); // string(6) "Google"</code>
スレッド関連のロジック(実行方法を定義することを含む)でクラスを汚染する責任は理想的ではないかもしれません。これらのクラスは、スレッドクラスを拡張してもらい、他のスレッドで実行することで分離できます。
<code class="language-php">class Task extends Threaded
{
public $response;
public function someWork()
{
$content = file_get_contents('http://google.com');
preg_match('~<title>(.+)</title>~', $content, $matches);
$this->response = $matches[1];
}
}
$task = new Task;
$thread = new class($task) extends Thread {
private $task;
public function __construct(Threaded $task)
{
$this->task = $task;
}
public function run()
{
$this->task->someWork();
}
};
$thread->start() && $thread->join();
var_dump($task->response);</code>
pthreadsによって公開されたクラスの階層をすばやく理解しましょう:スレッドをリサイクル
<code class="language-php">$task = new class extends Thread {
private $response;
public function run()
{
$content = file_get_contents("http://google.com");
preg_match("~<title>(.+)</title>~", $content, $matches);
$this->response = $matches[1];
}
};
$task->start() && $task->join();
var_dump($task->response); // string(6) "Google"</code>
<code class="language-php">class Task extends Threaded
{
public $response;
public function someWork()
{
$content = file_get_contents('http://google.com');
preg_match('~<title>(.+)</title>~', $content, $matches);
$this->response = $matches[1];
}
}
$task = new Task;
$thread = new class($task) extends Thread {
private $task;
public function __construct(Threaded $task)
{
$this->task = $task;
}
public function run()
{
$this->task->someWork();
}
};
$thread->start() && $thread->join();
var_dump($task->response);</code>
pthreadsおよび(非)変動性
<code class="language-php">$task = new class extends Thread {
private $response;
public function run()
{
$content = file_get_contents("http://google.com");
preg_match("~<title>(.+)</title>~", $content, $matches);
$this->response = $matches[1];
}
};
$task->start() && $task->join();
var_dump($task->response); // string(6) "Google"</code>
一方、揮発性クラスのねじれたプロパティは可変です:<code class="language-php">class Task extends Threaded
{
public $response;
public function someWork()
{
$content = file_get_contents('http://google.com');
preg_match('~<title>(.+)</title>~', $content, $matches);
$this->response = $matches[1];
}
}
$task = new Task;
$thread = new class($task) extends Thread {
private $task;
public function __construct(Threaded $task)
{
$this->task = $task;
}
public function run()
{
$this->task->someWork();
}
};
$thread->start() && $thread->join();
var_dump($task->response);</code>
揮発性クラスは、親クラスのスレッドクラスによって実施された不変性を無効にして、スレッドされたプロパティを再配分(および設定)できることがわかります。 <code>Threaded (implements Traversable, Collectable)
Thread
Worker
Volatile
Pool</code>
揮発性オブジェクトは、アレイベースの操作([])のサポート(上記のように)のサポートを提供するため、アレイのように扱うことができることがわかります。ただし、揮発性クラスは、array_popやarray_shiftなどの一般的な配列ベースの関数によってサポートされていません。代わりに、スレッドクラスは、これらの操作を組み込みの方法として提供します。 <code class="language-php">class Task extends Threaded
{
private $value;
public function __construct(int $i)
{
$this->value = $i;
}
public function run()
{
usleep(250000);
echo "Task: {$this->value}\n";
}
}
$worker = new Worker();
$worker->start();
for ($i = 0; $i < 15; $i++) {
$worker->stack(new Task($i));
}
while ($worker->collect());
$worker->shutdown();</code>
その他のサポートされている操作には、スレッド:: chunkおよびthreaded :: mergeが含まれます。
この記事で紹介する最後のトピックは、pthreadsでの同期です。同期は、共有リソースへの制御アクセスを可能にするテクノロジーです。
たとえば、<code class="language-php">class Task extends Threaded
{
private $value;
public function __construct(int $i)
{
$this->value = $i;
}
public function run()
{
usleep(250000);
echo "Task: {$this->value}\n";
}
}
$pool = new Pool(4);
for ($i = 0; $i < 15; $i++) {
$pool->submit(new Task($i));
}
while ($pool->collect());
$pool->shutdown();</code>
<code class="language-php">class Task extends Threaded // a Threaded class
{
public function __construct()
{
$this->data = new Threaded();
// $this->data is not overwritable, since it is a Threaded property of a Threaded class
}
}
$task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded
public function __construct($tm)
{
$this->threadedMember = $tm;
var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {}
$this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class
}
};</code>
<code class="language-php">class Task extends Volatile
{
public function __construct()
{
$this->data = new Threaded();
$this->data = new StdClass(); // valid, since we are in a volatile class
}
}
$task = new class(new Task()) extends Thread {
public function __construct($vm)
{
$this->volatileMember = $vm;
var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {}
// still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class
$this->volatileMember = new StdClass();
}
};</code>
が条件が真であることを指定したときに回復するために同期コールバックのみを許可するため、重要です。通知はスレッド::通知通話の外側から来る可能性があるため、これは重要です。したがって、スレッド:: waitへの呼び出しが条件に含まれていない場合、結論
FAQ(FAQ)
PHPでpthreadsを使用するための前提条件は何ですか?
phpでpthreadsを使用するには、PHPとオブジェクト指向のプログラミングに関する実用的な知識が必要です。また、ZTS(Zend Thread Safety)対応のPHPをインストールする必要があります。 Pthreadsは、標準のPHPインストールでは利用できません。端末でコマンド「PHP -I | GREP」スレッド安全 "」を実行することにより、PHPのインストールにZTSが有効になっているかどうかを確認できます。 「Thread Safety =&gt; enabled」を返す場合、Pthreadsを使用できます。
PTHREADSをインストールするには、PHP拡張コミュニティライブラリであるPECLを使用する必要があります。まず、ZTSを有効にするPHPがインストールされていることを確認してください。次に、端末で、コマンド「pecl install pthreads」を実行します。インストールが成功した場合、php.iniファイルに「extension = pthreads.so」行を追加する必要があります。これにより、PHPが実行されるたびにPthreads拡張子がロードされます。
新しいスレッドを作成するには、pthreadsが提供するスレッドクラスを拡張するクラスを定義する必要があります。このクラスでは、run()メソッドをオーバーライドします。これは、新しいスレッドで実行されるコードです。その後、このクラスのインスタンスを作成し、start()メソッドを呼び出して新しいスレッドを開始できます。
pthreadsは、スレッド間でデータを共有するためのスレッドクラスを提供します。このクラスの新しいインスタンスを作成して、スレッドに渡すことができます。このオブジェクトに設定されたプロパティは、スレッド間で安全に共有されます。
PTHREADSでのエラー処理は、標準のPHPでのエラー処理に似ています。トライキャッチブロックを使用して例外をキャッチできます。ただし、各スレッドには独自のスコープがあるため、1つのスレッドの例外は他のスレッドに影響しないことに注意してください。
pthreadsは、LaravelやSymfonyなどのPHPフレームワークと互換性がありません。これは、これらのフレームワークがスレッドセーフになるように設計されていないためです。これらのフレームワークで並列処理を実行する必要がある場合は、キューや非同期タスクなどの他の手法の使用を検討してください。
pthreadsを使用してphpスクリプトをデバッグする方法は?
pthreadsを使用したPHPスクリプトのデバッグは、各スレッドが独自のコンテキストで実行されるため、困難な場合があります。ただし、データの記録やデータの出力などの標準的なデバッグ手法をコンソールに使用できます。 XdebugのようなPHPデバッガーを使用することもできますが、すべてのデバッガーがマルチスレッドアプリケーションをサポートしているわけではないことに注意してください。
pthreadsは、Webサーバー環境では推奨されません。 CLI(コマンドラインインターフェイス)スクリプト用に設計されています。 Webサーバー環境でPthreadsを使用すると、予測不可能な結果につながる可能性があり、しばしば安全ではありません。
実行中のスレッドを停止するには、pthreadsが提供するkill()メソッドを使用できます。ただし、この方法は、スレッドが動作している場合に予測不可能な結果につながる可能性があるため、注意して使用する必要があります。通常、スレッドを設計して、タスクをきれいに完了できるようにすることが最善です。
はい、PHPでの並列プログラミングのためのpthreadsにはいくつかの代替品があります。これらには、子どものプロセスを作成および管理するためのインターフェイスを提供するPECL拡張機能と、PHP 7.2に導入されたネイティブPHP拡張機能を提供するPECL拡張機能が含まれます。
以上がPHPのPthreadsを使用した並列プログラミング - 基礎の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。