PHP の分野では、マルチスレッドの概念は他の言語ほどよく知られていません。 PHP は一般的にシングルスレッドモデルであり、マルチスレッド分野には向かないと考えていました。いくつかのマルチスレッド プロジェクトのソース コードを調べたところ、PHP のマルチスレッドにも素晴らしい用途があることがわかりました。柔軟に使用すると、特定の問題の解決に非常に適していることがわかります。
マルチスレッド
スレッド
まずスレッドについて話しましょう:
スレッドは、オペレーティングシステムが計算スケジュールを実行できる最小単位です。これはプロセスに含まれており、プロセス内の実際の操作単位となります。スレッドとは、プロセス内の単一の逐次的な制御フローを指します。プロセス内で複数のスレッドを同時に実行でき、各スレッドは異なるタスクを並行して実行します。
マルチスレッドを使用することには、実行効率に大きな利点があることが主な理由です。スレッドはオペレーティング システムがスケジュールできる最小単位であるため:
マルチスレッド プログラムはシングルスレッド プログラムよりもオペレーティング システムによってスケジュールされる可能性が高いため、一般にマルチスレッド プログラムの方がシングル スレッド プログラムよりも効率的です。 -スレッド化されたプログラム;
マルチスレッド プログラム マルチコア CPU の複数のコアで同時に実行でき、マルチコア マシンを最大限に活用できます。
マルチプロセス プログラムを同時に比較します。 -threading には次の特徴があります:
スレッドの作成と切り替えのシステム オーバーヘッドがプロセスのオーバーヘッドよりも高い
スレッドは本質的にメモリ空間を共有するため、スレッドは小さい必要があります。 、スレッド間の通信がより簡単になり、プロセス IPC による新たな複雑さの導入が回避されます。
適用可能なシナリオ
マルチスレッドには多くの最適化がありますが、スレッドの作成と破棄、コンテキストの切り替え、スレッドの同期なども行われるため、何も考えずにマルチスレッドを使用してもプログラムの実行効率を向上させることはできません。パフォーマンスが低下し、連続して実行するコードが増えるため、消費時間が長くなる可能性があります。例:
sumSmall は 1 から 50000 まで累積する関数です。
上の図は、メインスレッドでsumSmallを3回実行した場合と、3つのスレッドでsumSmallをそれぞれ実行した場合の結果を1つのスレッドに同期した場合の実行時間を比較したものです。 3 スレッドの作成、切り替え、同期に必要な時間は、スレッドの非同期実行によって節約される時間よりもはるかに長くなります。
関数 sumLarge は 1 から 5000000 まで累積します。次の図は、同じスレッドを 3 回実行し、3 つのスレッドを実行するのにかかる時間を示しています。
今回は、ついにマルチスレッドにより効率が向上しました。
マルチスレッドを使用するかどうかは、特定のニーズに応じて決定する必要があります。一般に、次の 2 つの状況を考慮する必要があります。
I/O ブロックにより、オペレーティング システムでタスクのスケジュールが設定され、現在のタスクがブロックされるため、コード内の I/O が多い場合、マルチスレッドを使用するとコードを並列化できます。たとえば、ファイル全体を複数回読み取る場合や、複数のネットワーク リソースを要求する場合などです。
マルチスレッドは CPU を最大限に活用できるため、上記の後者の例のように、計算負荷の高いコードが複数ある場合は、マルチスレッドを使用してそれらを並列実行することもできます。
PHP のマルチスレッド
PHP はデフォルトではマルチスレッドをサポートしていません。マルチスレッドを使用するには、pthread 拡張機能をインストールする必要があります。 --enable-maintainer-zts を使用する必要があります。 PHP を再コンパイルするためのパラメータ このパラメータは、PHP をコンパイルするときにスレッドセーフを使用するように指定されます。
スレッド セーフティ
マルチスレッドはプログラムを不安定にする要因です。マルチスレッドを使用する前に、まずスレッド セーフティの問題を考慮する必要があります。
スレッド セーフティ: スレッド セーフティとは、プログラミングにおける用語で、特定の関数を指します。 , マルチスレッド環境で関数ライブラリを呼び出すと、複数のスレッド間の共有変数を正しく処理できるため、プログラム関数を正しく完了できます。
従来のマルチスレッドでは、複数のスレッドが変数を共有するため、次の問題が発生する可能性があります:
グローバル配列 $arr = array('a');; がある
スレッドは長さ 1 の配列を取得します;
B スレッドは配列の長さを 1 にします;
A スレッドは配列要素 $a = array_pop($arr); $a = 'a';;
B スレッドも配列要素 $ をポップしますb = array_pop($arr ); $a = null;;
このとき、配列の長さが明らかに 0 より大きいか、何かがポップされていませんでした。
PHP 実装
PHP によって実装されるスレッド セーフティは、主に TSRM メカニズムを使用してグローバル変数と静的変数が分離され、各スレッドがメイン スレッドのバックアップを使用するため、変数の競合が回避され、スレッド セーフティが妨げられます。質問。
PHP のマルチスレッドのカプセル化により、プログラマは読み取りと書き込みの競合を回避するために、グローバル変数にさまざまなロックを追加することを考慮する必要がなくなります。また、エラーの可能性が減り、記述されたコードがより安全になります。
しかし、その結果、サブスレッドが実行を開始すると、メインスレッドはサブスレッドの実行の詳細を調整できなくなり、スレッドはグローバル変数を介してスレッド間でメッセージを送信する機能をある程度まで失います。
同時に、PHP がスレッド セーフ オプションをオンにした後、TSRM メカニズムを使用して変数を割り当てて使用するときに追加の損失が発生するため、マルチスレッドを必要としない PHP 環境では、ZTS (非スレッド セーフ) バージョンの PHP。
クラスとメソッド
PHP はスレッドを Thread クラスにカプセル化します。スレッドの作成は、クラスのカプセル化により、コンストラクターを介してのみ渡すことができ、スレッドの操作結果も必要になります。クラス変数を通じて渡されます。
一般的に使用される Thread クラスのメソッドをいくつか示します:
run():此方法是一个抽象方法,每个线程都要实现此方法,线程开始运行后,此方法中的代码会自动执行; start():在主线程内调用此方法以开始运行一个线程; join():各个线程相对于主线程都是异步执行,调用此方法会等待线程执行结束; kill():强制线程结束; isRunning():返回线程的运行状态,线程正在执行run()方法的代码时会返回 true;
スレッド セーフの実装により、PHP のマルチスレッドが実行を開始すると、共有メモリ空間を介して通信できなくなり、スレッド間でスレッドを再利用できなくなります。したがって、PHP の「スレッド プール」はあまり意味がないと思います。拡張機能に付属するPoolクラスはマルチスレッドの割り当てを管理するクラスなのでここでは紹介しません。
サンプルコード
以下は、特定のインターフェースをリクエストするために使用されるスレッドクラスです。次に、これに基づいて 2 つのマルチスレッド アプリケーションの例を作成します。
class Request extends Thread { public $url; public $response; public function __construct($url) { $this->url = $url; } public function run() { $this->response = file_get_contents($this->url); } }
非同期リクエスト
同期リクエストを複数のスレッドと非同期呼び出しに分割して、プログラムの実行効率を向上させます。
$chG = new Request("www.google.com"); $chB = new Request("www.baidu.com"); $chG ->start(); $chB ->start(); $chG->join(); $chB->join(); $gl = $chG->response; $bd = $chB->response;
タイムアウト制御
会社の Web サイトの特定の Web ページのコンテンツの一部が断続的であることを偶然発見しました。具体的な実装はわかりませんが、これがマルチスレッドを使用するインスピレーションを与えてくれました。スレッドの非同期を使用します。迅速な障害とタイムアウトの制御を実現します。
curl を使用してアドレスをリクエストする場合、CURLOPT_CONNECTTIMEOUT / CURLOPT_TIMEOUT パラメーターを使用して、curl の接続タイムアウトと読み取りデータ タイムアウトをそれぞれ設定できますが、合計タイムアウトを制御するのは困難です。さらに、データベース クエリを実行するときにタイムアウト期間を設定することはできません (Niao Ge のブログ: MySQL のクエリ タイムアウトの設定)。
現時点では、マルチスレッドを使用してこの関数を実装できます。スレッド クラスの start() メソッドを実行した後は、join() メソッドを呼び出さないようにします。これにより、スレッドは非同期状態のままになり、メインスレッドの実行をブロックします。
このとき、メインスレッドは旗艦に相当し、各サブスレッドは巡洋艦に相当します。旗艦が特定の場所に到着した後は、巡洋艦の帰還を待つ必要はありません。一定時間待ってから離れることで、巡洋艦が予想外に待機したときに旗艦が空になることを防ぎます。
コード:
$chG = new Request("www.google.com"); $chB = new Request("www.baidu.com"); $chG->start(); $chB->start(); $chB->join(); // 此处不对chG执行join方法 sleep(1); // sleep一个能接受的超时时间 $gl = $chG->response; $bd = $chB->response; $bd->kill(); if (!$gl) { $gl = ""; // 处理异常,或在线程类内给$gl一个默认值 }
概要
PHP のマルチスレッドのシール (ヤン) インストール (ゲ) は、スレッドを使用するときに人々を非常に不快にさせます。安全であり、PHP のシンプルで使いやすいスタイルを維持していますが、マルチスレッド機能を完全に活用することはできません。ただし、それぞれの言語には独自の特徴と強調点があるため、彼女を愛している場合は、彼女を許容する必要がありますので、強制する必要はありません。
関連する推奨事項:
以上がPHP のマルチスレッド プログラミングのサポートと使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。