ホームページ >バックエンド開発 >PHPチュートリアル >PHPのPthreadsを使用した並列プログラミング - 基礎

PHPのPthreadsを使用した並列プログラミング - 基礎

Jennifer Aniston
Jennifer Anistonオリジナル
2025-02-10 08:57:09618ブラウズ

Parallel Programming with Pthreads in PHP - the Fundamentals

キーポイント

  • Webサーバー環境でpthreadsを使用しないでください:セキュリティとスケーラビリティの問題により、これらの環境スレッドで複数の環境を効果的に処理できないため、FCGIなどのWebサーバー環境ではPTHREADを使用しないでください。
  • 1回限りのタスクまたはIOバインディング操作にPTHREADSを使用します:
  • 1つを実行するか、多数のIO操作を必要とするタスクの場合、PTHREADSを使用すると、メインの実行スレッドをアンインストールして個別に処理することができますスレッドこれらの操作は、パフォーマンスを改善するために使用されます。
  • リサイクルリソースをリサイクルします。
  • 各タスクの新しいスレッドを作成すると、多くのリソースを使用できます。
  • PTHREADSと揮発性クラスの不変性を理解する:
  • スレッドを拡張するオブジェクトの特性は、パフォーマンスの分解を避けるために不可能であり、揮発性クラスは必要に応じて変化する方法を提供します属性の。
  • スレッドの安全性の同期を実装:
  • データの腐敗を防ぎ、複数のスレッドが共有リソースにアクセスした場合に一貫した結果を確保するには、同期ブロックやスレッドなどのpthreadsが提供する同期方法を使用します:: wait and threaded: :通知およびその他の方法。
この記事はクリストファー・ピットによってレビューされました。 SitePointコンテンツを完璧にしてくれたすべてのSitePointピアレビュアーに感謝します!

PHP開発者は、並列性をめったに利用しないようです。同期のシングルスレッドプログラミングのシンプルさは非常に魅力的ですが、少し並行性を使用すると、価値のあるパフォーマンスの改善につながる場合があります。
この記事では、PTHREADS拡張機能を使用してPHPにスレッドを実装する方法を学びます。これには、PHP 7.xのZTS(Zend Thread-Safe)バージョンのインストールと、Pthreads V3のインストールが必要です。 (執筆時点で、PHP 7.1ユーザーはPTHREADSレポのマスターブランチからインストールする必要があります - ソースからのサードパーティの拡張機能の構築の詳細については、この記事の一部を参照してください。)

簡単な説明:Pthreads V2はPHP 5.xを対象としており、PTHREADS V3はPHP 7.xを対象としており、アクティブな開発中です。

私の投稿を校正し、改善してくれたJoe Watkins(Pthreads拡張機能の作成者)に感謝します!

pthreadsを使用しない場合Parallel Programming with Pthreads in PHP - the Fundamentals

先に進む前に、最初に

(および

)を使用してはならない状況を説明したいと思います。 pthreads v2では、Webサーバー環境(つまり、FCGIプロセス)でpthreadsを使用することをお勧めしません。 Pthreads v3から始めて、この提案は実施されるため、Webサーバー環境ではまったく使用できません。これを行う主な2つの理由は、です

  1. この環境で複数のスレッドを使用することは安全ではありません(IOの問題やその他の問題を引き起こす可能性があります)。
  2. それはうまくスケーリングしません。たとえば、いくつかの作業を処理する新しいスレッドを作成するPHPスクリプトがあり、そのスクリプトが要求するたびに実行されるとします。これは、各リクエストに対して、アプリケーションが新しいスレッドを作成することを意味します(これは1:1スレッドモデルです。1つのスレッドは1つのリクエストに対応します)。申請が1秒あたり1,000個のリクエストを処理すると、1秒あたり1,000個のスレッドが作成されます!単一のマシンで非常に多くのスレッドを実行すると、すぐに圧倒されます。この問題は、要求レートが上昇すると悪化します。
これが、この環境でスレッドが良い解決策ではない理由です。 IOブロッキングタスク(HTTPリクエストの実行など)としてスレッドのソリューションを探している場合は、AMPなどのフレームワークを通じて達成できる

非同期プログラミングの方向を指摘させてください。 SitePointは、興味があれば、このトピックに関するいくつかの優れた記事(非同期ライブラリの作成やMinecraftの修正など)を公開しています。 ポイントに戻って、トピックにまっすぐに行きましょう!

1回限りのタスクのトレーニング

マルチスレッドの方法で1回限りのタスクを処理したい場合があります(IOバインドタスクの実行など)。この場合、スレッドクラスを使用して新しいスレッドを作成し、その別のスレッドでいくつかの作業単位を実行できます。

例:

上記の上記の実行方法は、新しいスレッドで実行する作業単位です。 Thread :: Startが呼び出されると、新しいスレッドが生成され、実行方法が呼び出されます。次に、生成されたスレッドをメインスレッド(スレッド::結合経由で)に再加入します。これは、個々のスレッドが実行を完了するまでブロックします。これにより、結果を出力しようとする前にタスクが実行を完了したことが保証されます($ task-> responseに保存)。

<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によって公開されたクラスの階層をすばやく理解しましょう:

スレッドクラスとスレッドクラスの基本をすでに学んでいるので、残りの3つ(労働者、揮発性、およびプール)を見てみましょう。

スレッドをリサイクル

各タスクが並列化されるために新しいスレッドを開始するのは費用がかかります。これは、PHP内にスレッドを実装するために、pthreadsが共有されたステートレスアーキテクチャを採用する必要があるためです。これは、PHPインタープリターの現在のインスタンスの実行コンテキスト全体(各クラス、インターフェイス、属性、および関数を含む)を、作成したスレッドごとにコピーする必要があることを意味します。これにはパフォーマンスが大きな影響を与える可能性があるため、スレッドは常に可能な限り再利用する必要があります。スレッドを再利用するには、ワーカーの使用またはプールの使用方法は2つあります。

ワーカークラスは、別のスレッドで一連のタスクを同期して実行するために使用されます。これは、新しいワーカーインスタンス(これにより新しいスレッドが作成されます)を作成し、その別のスレッドにタスクを積み重ねることによって行われます(ワーカー::スタックを介して)。

これは簡単な例です:

<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>

output:

Parallel Programming with Pthreads in PHP - the Fundamentals

上記のスタック15のタスクは、ワーカー::スタックを介して新しい$ Workerオブジェクトにタスクを設定し、スタッキング順序で処理します。上記のように、ワーカー::収集方法は、タスクが実行を完了した後にタスクをクリーンアップするために使用されます。 whileループでそれを使用することにより、すべての積み重ねられたタスクが実行され、クリーンアップされるまでメインスレッドをブロックし、その後、ワーカー::シャットダウンをトリガーします。ワーカーを時期尚早に閉じる(つまり、タスクを実行する必要がありますが)すべてのタスクが実行されるまでメインスレッドをブロックします - タスクはガベージが収集されません(メモリリークを引き起こします)。

ワーカークラスは、最古のスタックアイテムを削除するためのワーカー:: unstack、およびスタック上のアイテムの数を実行するためにgetStackedを含む、他のタスクスタック関連の方法を提供します。ワーカーのスタックは、実行するタスクを保存するだけです。スタック内のタスクが実行されると、削除され、ごみ収集の別の(内部)スタックに配置されます(ワーカー::コレクションを使用)。

多くのタスクを実行するときにスレッドを再利用する別の方法は、スレッドプールを使用することです(プールクラスを介して)。スレッドプールは、タスクを同時に実行できるようにするために一連のワーカーによって駆動されます。ここでは、プールの作成時に並行性係数(プールが実行されるスレッドの数)が指定されます。

上記の例を調整して、ワーカープールを使用しましょう。

output:

<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>

プールの使用とワーカープログラムの使用には、いくつかの大きな違いがあります。まず、プールを手動で開始する必要はありません。利用可能になり次第、タスクの実行を開始します。第二に、タスクを積み重ねる代わりにプールに提出します。さらに、プールクラスはスレッドに拡張されていないため、他のスレッドに渡されない場合があります(ワーカーとは異なります)。

Parallel Programming with Pthreads in PHP - the Fundamentals 良い実践として、タスクを完了した後、常に労働者プログラムやプールのタスクを収集し、手動で閉じる必要があります。スレッドクラスを介して作成されたスレッドも、作成者スレッドに再会する必要があります。

pthreadsおよび(非)変動性

導入される最後のクラスは揮発性です。これは、pthreads v3への新しい追加です。 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の配列は揮発性オブジェクトに自動的にキャストされます。これは、PHPの複数のコンテキストから配列を操作することが安全ではないためです。

よりよく理解するために、もう一度例をもう一度見てみましょう:

<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>

正しい出力を取得するように同期を追加してこれを修正しましょう20:

同期コードブロックは、スレッド:: wait and threaded :: notify(およびthreaded :: notifyone)でも動作できます。
<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>

以下は、2つの同期中のループからのインターリーブの増分です。

スレッド:: waitの呼び出しの周りに追加の条件が追加されていることに気付いたかもしれません。これらの条件は、通知が

および
<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への呼び出しが条件に含まれていない場合、

虚偽のウェイクアップコールに対して脆弱になります。これにより、コードが予測不可能になります。

結論

各クラスの使用を導入するなど、Pthreadsが付属する5つのクラス(スレッド、スレッド、ワーカー、揮発性、プール)が見られました。また、Pthreadsの不変性の新しい概念と、サポートする同期機能をすばやく調べました。これらの基本がカバーされているので、いくつかの実際のユースケースにpthreadsを適用することを検討することができます!これが次の投稿の主題になります。

同時に、PTHREADSのアプリケーションのアイデアがある場合は、以下のコメントセクションにご意見をお聞かせください!

php

のpthreadsを使用した並列プログラミングに関する

FAQ(FAQ)

PHPでpthreadsを使用するための前提条件は何ですか?

phpでpthreadsを使用するには、PHPとオブジェクト指向のプログラミングに関する実用的な知識が必要です。また、ZTS(Zend Thread Safety)対応のPHPをインストールする必要があります。 Pthreadsは、標準のPHPインストールでは利用できません。端末でコマンド「PHP -I | GREP」スレッド安全 "」を実行することにより、PHPのインストールにZTSが有効になっているかどうかを確認できます。 「Thread Safety =&gt; enabled」を返す場合、Pthreadsを使用できます。

phpにpthreadsをインストールする方法は?

PTHREADSをインストールするには、PHP拡張コミュニティライブラリであるPECLを使用する必要があります。まず、ZTSを有効にするPHPがインストールされていることを確認してください。次に、端末で、コマンド「pecl install pthreads」を実行します。インストールが成功した場合、php.iniファイルに「extension = pthreads.so」行を追加する必要があります。これにより、PHPが実行されるたびにPthreads拡張子がロードされます。

pthreadsを使用してPHPで新しいスレッドを作成する方法は?

新しいスレッドを作成するには、pthreadsが提供するスレッドクラスを拡張するクラスを定義する必要があります。このクラスでは、run()メソッドをオーバーライドします。これは、新しいスレッドで実行されるコードです。その後、このクラスのインスタンスを作成し、start()メソッドを呼び出して新しいスレッドを開始できます。

phpのスレッド間でデータを共有するためにpthreadsを使用する方法は?

pthreadsは、スレッド間でデータを共有するためのスレッドクラスを提供します。このクラスの新しいインスタンスを作成して、スレッドに渡すことができます。このオブジェクトに設定されたプロパティは、スレッド間で安全に共有されます。

pthreadsのエラーを処理する方法は?

PTHREADSでのエラー処理は、標準のPHPでのエラー処理に似ています。トライキャッチブロックを使用して例外をキャッチできます。ただし、各スレッドには独自のスコープがあるため、1つのスレッドの例外は他のスレッドに影響しないことに注意してください。

LaravelやSymfonyなどのPHPフレームワークでpthreadsを使用できますか?

pthreadsは、LaravelやSymfonyなどのPHPフレームワークと互換性がありません。これは、これらのフレームワークがスレッドセーフになるように設計されていないためです。これらのフレームワークで並列処理を実行する必要がある場合は、キューや非同期タスクなどの他の手法の使用を検討してください。

pthreadsを使用してphpスクリプトをデバッグする方法は?

pthreadsを使用したPHPスクリプトのデバッグは、各スレッドが独自のコンテキストで実行されるため、困難な場合があります。ただし、データの記録やデータの出力などの標準的なデバッグ手法をコンソールに使用できます。 XdebugのようなPHPデバッガーを使用することもできますが、すべてのデバッガーがマルチスレッドアプリケーションをサポートしているわけではないことに注意してください。

Webサーバー環境でPTHREADSを使用できますか?

pthreadsは、Webサーバー環境では推奨されません。 CLI(コマンドラインインターフェイス)スクリプト用に設計されています。 Webサーバー環境でPthreadsを使用すると、予測不可能な結果につながる可能性があり、しばしば安全ではありません。

pthreadsを使用してPHPで実行されているスレッドを停止する方法は?

実行中のスレッドを停止するには、pthreadsが提供するkill()メソッドを使用できます。ただし、この方法は、スレッドが動作している場合に予測不可能な結果につながる可能性があるため、注意して使用する必要があります。通常、スレッドを設計して、タスクをきれいに完了できるようにすることが最善です。

PHPでの並列プログラミングのためのpthreadsに代わるものはありますか?

はい、PHPでの並列プログラミングのためのpthreadsにはいくつかの代替品があります。これらには、子どものプロセスを作成および管理するためのインターフェイスを提供するPECL拡張機能と、PHP 7.2に導入されたネイティブPHP拡張機能を提供するPECL拡張機能が含まれます。

以上がPHPのPthreadsを使用した並列プログラミング - 基礎の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。