PHPにはスレッドがありますか?


130

スレッドと呼ばれるこのPECLパッケージを見つけましたが、まだリリースはありません。そして、PHPのWebサイトでは何も予定されていません。


pcntl_fork()Apacheから呼び出された場合、これ()が機能するかどうか誰でも知っていますか?
ジョシュK

これは信じられないほど古いですが、実際にphpでスレッドを提供する答えがあります(リンクについては以下を参照)。
アレック渓谷、

サーバー環境からforkを呼び出さないことをお勧めします。私は彼らを責めません。ただし、pcntl_forkはPHPのスレッド化に最適なソリューションのようです。
just_wes

はい、apache2 phpプロセスをフォークする必要はありません。
andho

2
pthreadの使用は魅力のように機能します
Baba

回答:


40

私が知っている利用可能なものは何もありません。次善の策は、CLIを介して1つのスクリプトで別のスクリプトを実行することですが、それは少し初歩的です。何をしようとしているか、どれほど複雑かによって、これはオプションになる場合も、そうでない場合もあります。


1
私もそう思っていました。古い投稿がたくさんあり、php.netには何も書かれていないのを見たので、これは私の考えでした。確認していただきありがとうございます。
Thomas Owens、

2
ええ、そのPECLパッケージはちょっといじめです-私もそれに遭遇しましたが、何もこれまで来ていません。
Wilco、

180

pthreads拡張機能のPHPマニュアルから:

pthreadsは、PHPでユーザーランドのマルチスレッドを可能にするオブジェクト指向APIです。Webまたはコンソールを対象としたマルチスレッドアプリケーションを作成するために必要なすべてのツールが含まれています。PHPアプリケーションは、スレッド、ワーカー、スタッカブルを作成、読み取り、書き込み、実行、同期できます。

これが信じられないほど信じられないほど、それは完全に本当です。今日、PHPは、試してみたい人のためにマルチスレッド化できます。

PHP4の最初のリリース、2000年5月22日、PHPはスレッドセーフアーキテクチャで出荷されました。これは、マルチスレッドSAPI(Server API)環境で別個のスレッドでインタプリタの複数のインスタンスを実行する方法です。過去13年間、このアーキテクチャの設計は維持され、進歩してきました。それ以来、世界最大のWebサイトで本番環境で使用されています。

ユーザーランドでのスレッディングは、PHPチームにとって決して問題ではありませんでしたが、今日もそうです。PHPがビジネスを行う世界では、ハードウェアを追加するという、定義されたスケーリング方法がすでに存在していることを理解する必要があります。長年にわたってPHPが存在し、ハードウェアがどんどん安くなっているため、これはPHPチームにとってますます懸念事項ではなくなりました。値段は安くなりましたが、さらに強力になりました。今日、私たちの携帯電話とタブレットはデュアルおよびクアッドコアアーキテクチャとそれに対応する十分なRAMを備えています。デスクトップとサーバーは通常8または16コア、16および32ギガバイトのRAMを備えていますが、常に2つを備えているとは限りません予算の範囲内で2つのデスクトップを持つことは、ほとんどの人にとってめったに役に立ちません。

さらに、PHPはプログラマーではない人のために書かれたもので、多くの愛好家のネイティブタンです。PHPが非常に簡単に採用される理由は、PHPが学習および記述が容易な言語だからです。PHPが今日非常に信頼できる理由は、PHPの設計に費やされる膨大な量の作業と、PHPグループによるすべての単一の決定によるものです。これらすべての年の後に、それは信頼性と純粋な偉大さがスポットライトでそれを保ちます。ライバルが時間やプレッシャーに陥っているところ。

マルチスレッドプログラミングは、最も一貫性のある信頼性の高いAPIを使用していても、ほとんどの場合簡単ではありません。さまざまなことを検討する必要があり、多くの誤解があります。PHPグループは、ユーザーランドのマルチスレッド化がコア機能であることを望んでいません。誰にとっても、PHPは複雑であってはなりません。

すべてを考慮した上で、PHPが本番環境の準備済みでテスト済みの機能を利用できるようにすることで得られるメリットはまだあります。本当に必要なタスクはありません。

pthreadsは、探索したい人のために、ユーザーがPHPアプリケーションをマルチスレッド化できるAPIを実現します。それはAPIが非常に進行中の作業であり、安定性と完全性のベータレベルを指定しました。

PHPが使用するライブラリの一部はスレッドセーフではないことはよく知られています。pthreadがこれを変更できないこと、および試そうとしないことはプログラマーには明らかです。ただし、インタプリタの他のスレッドセーフなセットアップと同様に、スレッドセーフなライブラリはすべて使用できます。

pthreadsはPosixスレッド(Windowsでも)を利用します。プログラマーが作成するのは実際の実行スレッドですが、それらのスレッドが有用であるためには、PHPを認識している必要があります-ユーザーコードの実行、変数の共有、および有用な通信手段の許可(同期)。したがって、すべてのスレッドはインタープリターのインスタンスで作成されますが、マルチスレッドのサーバーAPI環境と同様に、そのインタープリターはインタープリターの他のすべてのインスタンスから分離されています。pthreadsは、適切かつ安全な方法でギャップを埋めようとします。Cのスレッドのプログラマーの懸念の多くは、pthreadのプログラマーにはまったくありません。設計上、pthreadは読み取り時にコピーされ、書き込み時にコピーされるため(RAMは安価です)、2つのインスタンスが同じ物理データを操作することはありません。ただし、どちらも別のスレッドのデータに影響を与える可能性があります。

読み取り時にコピーし、書き込み時にコピーする理由:

public function run() {
    ...
    (1) $this->data = $data;
    ...
    (2) $this->other = someOperation($this->data);
    ...
}

(3) echo preg_match($pattern, $replace, $thread->data);

(1)読み取りおよび書き込みロックがpthreadオブジェクトデータストアに保持されている間、データはメモリ内の元の場所からオブジェクトストアにコピーされます。pthreadは変数のrefcountを調整しません。Zendは、それ以上の参照がない場合、元のデータを解放できます。

(2)someOperationの引数は、オブジェクトストアを参照します。これは、格納された元のデータであり、それ自体が(1)の結果のコピーであり、エンジンのzvalコンテナーに再度コピーされますが、これが発生すると、読み取りロックが保持されます。オブジェクトストア、ロックが解放され、エンジンは関数を実行できます。zvalが作成されると、参照カウントは0になり、他の参照が存在しないため、操作の完了時にエンジンがコピーを解放できるようになります。

(3)preg_matchの最後の引数がデータストアを参照し、読み取りロックが取得され、(1)で設定されたデータがzvalにコピーされ、refcountが0になります。ロックが解除され、preg_matchの呼び出しが処理されますデータのコピー、それ自体が元のデータのコピーです。

知っておくべきこと:

  • データが格納されるオブジェクトストアのハッシュテーブルは、スレッドセーフであり
    、PHPに同梱されているZendのTsHashTableに基づいています。

  • オブジェクトストアには読み取りロックと書き込みロックがあり、TsHashTableには追加のアクセスロックが用意されているため、必要に応じて(PHPエンジンがプロパティを参照するときにvar_dump / print_r、プロパティに直接アクセスできます)、pthreadはTsHashTableを操作できます。定義されたAPIの外。

  • ロックは、コピー操作が行われている間のみ保持され、コピーが作成されると、適切な順序でロックが解除されます。

これの意味は:

  • 書き込みが発生すると、読み取りおよび書き込みロックだけでなく、追加のアクセスロックも保持されます。テーブル自体はロックされています。別のコンテキストがテーブルをロック、読み取り、書き込み、または影響する可能性はありません。

  • 読み取りが発生すると、読み取りロックが保持されるだけでなく、追加のアクセスロックも保持され、再びテーブルがロックダウンされます。

2つのコンテキストがオブジェクトストアから同じデータに物理的にも同時にもアクセスすることはできませんが、参照のあるコンテキストで行われた書き込みは、参照のあるコンテキストで読み取られたデータに影響します。

これは何も共有しないアーキテクチャであり、存在する唯一の方法は共存することです。少し精通している人はそれを見るでしょう、ここで多くのコピーが行われている、そして彼らはそれが良いことかどうか疑問に思うでしょう。動的ランタイム内ではかなりの量のコピーが行われます。それが動的言語のダイナミクスです。1つのオブジェクトを適切に制御できるため、pthreadはオブジェクトのレベルで実装されますが、メソッド(プログラマが実行するコード)には、ロックやコピーのない別のコンテキスト(ローカルメソッドスコープ)があります。pthreadsオブジェクトの場合のオブジェクトスコープは、コンテキスト間でデータを共有する方法として扱う必要があります。それが目的です。これを念頭に置いて、必要でない限り、オブジェクトストアのロックを回避する手法を採用できます。

PHPで使用できるライブラリと拡張機能のほとんどはサードパーティの薄いラッパーであり、PHPのコア機能はある程度同じです。pthreadsは、Posixスレッドの薄いラッパーではありません。これは、Posixスレッドに基づくスレッドAPIです。PHPでスレッドを実装しても、ユーザーが理解していない、または使用できないのは意味がありません。ミューテックスとは何か、またはミューテックスについて何も知らない人が、スキルとリソースの両方の面で、ミューテックスが持っているすべてのものを利用できないはずがあるという理由はありません。オブジェクトはオブジェクトのように機能しますが、2つのコンテキストが衝突する場合は常に、pthreadは安定性と安全性を提供します。

Javaで作業したことがある人は誰でも、pthreadsオブジェクトとJavaでのスレッド化の類似点を確認できます。同じ人々は、ConcurrentModificationExceptionと呼ばれるエラーを見たことがあるでしょう。同時に。それが存在する理由は理解していますが、リソースができるだけ安価であり、ランタイムがユーザーに対して安全性を達成できる正確かつ唯一のタイミングで並行性を検出できるという事実と相まって、困惑しています実行とデータへのアクセスを管理するのではなく、実行時に致命的なエラーをスローします。

そのような愚かなエラーはpthreadによって出力されることはなく、APIはスレッド化を可能な限り安定させ、互換性を持たせるように作成されていると思います。

マルチスレッドは新しいデータベースの使用とは異なります。pthreadに同梱されているマニュアルと例のすべての単語に細心の注意を払う必要があります。

最後に、PHPマニュアルから:

pthreadsは、非常に良い結果をもたらす実験でした。その制限や機能はいつでも変更される可能性があります。それが実験の性質です。これは、多くの場合、実装によって課せられる制限ですが、正当な理由があるためです。pthreadの目的は、任意のレベルでPHPのマルチタスクに使用可能なソリューションを提供することです。pthreadが実行する環境では、安定した環境を提供するためにいくつかの制限事項があります。


14
全くのナンセンス。
Joe Watkins

7
@GeoC。私はあなたのポイントがここに何であるかさえわからない、それは単なる意味不明なことであり、あなたはいかなる論拠にも同意しない理由について論理的またはその他の理由を提供しません
神保

13
@チューダーあなたが何を話しているのか本当にわからないと思いますので、無視させていただきます。
ジョーワトキンス

4
@チューダー-多くの科学者は、彼らのブランチに新しい何か、または何か有用なものの「要点を理解」していませんでした。歴史は、たいていの場合、あなた自身のような人々が単に間違っていることを示しています、それは事実です。あなたがここに書いたすべてが、より良い言葉が欠けていると、糞便であるという事実のように。すべてを前向きに考え、何も知らないトピック(これは1つです)には参加しないことを強くお勧めします。
NB

12
面白いこと。Joe Watkinsはpthreadsの作者ですが、それでもTudorは彼の間違いを証明しようとします。
Hristo Valkanov

48

ウィルコが提案した例を以下に示します。

$cmd = 'nohup nice -n 10 /usr/bin/php -c /path/to/php.ini -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & echo $!';
$pid = shell_exec($cmd);

基本的に、これはコマンドラインでPHPスクリプトを実行しますが、すぐにPIDを返し、バックグラウンドで実行します。(エコー$!は、PID以外の何も返されないことを保証します。)これにより、必要に応じてPHPスクリプトを続行または終了できます。これを使用したとき、ユーザーを別のページにリダイレクトしました。5〜60秒ごとに、レポートがまだ実行されているかどうかを確認するためにAJAX呼び出しが行われます。(gen_idとそれに関連するユーザーを格納するテーブルがあります。)チェックスクリプトは以下を実行します。

exec('ps ' . $pid , $processState);
if (count($processState) < 2) {
     // less than 2 rows in the ps, therefore report is complete
}

このテクニックに関する短い投稿がここにあります:http : //nsaunders.wordpress.com/2007/01/12/running-a-background-process-in-php/


わずかな問題があります。バックグラウンドプロセスのステータスを確認するにはどうすればよいですか?私を啓発することができます
サイア、2013年

25

つまり、phpにはマルチスレッドがありますが、代わりにマルチプロセッシングを使用する必要があります。

背景情報:スレッドとプロセス

スレッドとプロセスの違いについては常に少し混乱があるので、両方について簡単に説明します。

  • スレッドは、 CPUが処理する一連のコマンドです。それを構成する唯一のデータは、プログラムカウンタです。各CPUコアは一度に1つのスレッドのみを処理しますが、スケジューリングを介して異なるスレッドの実行を切り替えることができます。
  • プロセスは、共有リソースのセットです。つまり、メモリ、変数、オブジェクトインスタンス、ファイルハンドル、ミューテックス、データベース接続などの一部で構成されています。各プロセスには、1つ以上のスレッドも含まれています。同じプロセスのすべてのスレッドはそのリソースを共有するため、あるスレッドで作成した変数を別のスレッドで使用できます。それらのスレッドが2つの異なるプロセスの一部である場合、他のリソースに直接アクセスすることはできません。この場合、パイプ、ファイル、ソケットなどを介したプロセス間通信が必要です...

マルチプロセッシング

phpで新しいプロセス(新しいスレッドも含む)を作成することで、並列計算を実現できます。スレッドが多くの通信や同期を必要としない場合は、これが選択です。これは、プロセスが分離され、互いの作業を妨げることがないためです。1つがクラッシュしても、それは他のユーザーには関係ありません。大量の通信が必要な場合は、「マルチスレッド化」を読むか、悲しいことに、別のプログラミング言語を使用することを検討してください。プロセス間通信と同期により多くの複雑さが生じるためです。

PHPでは、新しいプロセスを作成する2つの方法があります。

OSに任せてください。オペレーティングシステムに新しいプロセスを作成し、その中で新しい(または同じ)phpスクリプトを実行するように指示できます。

  • 以下のためのLinuxの次を使用するか検討することができダリル・ハインの答えを

    $cmd = 'nice php script.php 2>&1 & echo $!';
    pclose(popen($cmd, 'r'));
  • 以下のための窓あなたはこれを使用することがあります。

    $cmd = 'start "processname" /MIN /belownormal cmd /c "script.php 2>&1"';
    pclose(popen($cmd, 'r'));

フォークを使用して自分で行う:phpは、関数pcntl_fork()を使用してフォークを使用することもできます。これを行う方法に関する優れたチュートリアルはここにありますが、フォークは人類に対する犯罪であり、特にoopに対する犯罪であるため、使用しないことを強くお勧めします。

マルチスレッド

マルチスレッドを使用すると、すべてのスレッドがリソースを共有するため、多くのオーバーヘッドなしでスレッド間の通信と同期を簡単に行うことができます。一方、競合状態とデッドロックは簡単に生成できますが、デバッグが非常に難しいため、何をしているのかを知る必要があります。

標準のphpはマルチスレッドを提供しませんが、実際に-pthreadsを実行する(実験的)拡張があります。そのapiドキュメントはphp.netに作成しました。これを使えば、実際のプログラミング言語でできるように、次のようなことができます。

class MyThread extends Thread {
    public function run(){
        //do something time consuming
    }
}

$t = new MyThread();
if($t->start()){
    while($t->isRunning()){
        echo ".";
        usleep(100);
    }
    $t->join();
}

以下のためのlinuxがあるインストールガイドのstackoverflowの時に右ここに。

以下のためのウィンドウになりましたものがあります。

  • まず、スレッドセーフなバージョンのphpが必要です。
  • pthreadとそのphp拡張の両方のコンパイル済みバージョンが必要です。こちらからダウンロードできます。phpのバージョンと互換性のあるバージョンをダウンロードしてください。
  • php_pthreads.dllを(ダウンロードしたzipから)php拡張フォルダー([phpDirectory] ​​/ ext)にコピーします。
  • pthreadVC2.dllを[phpDirectory](ルートフォルダー-拡張フォルダーではない)にコピーします。
  • [phpDirectory] ​​/php.iniを編集し、次の行を挿入します

    extension=php_pthreads.dll
  • 上記のスクリプトを使用して、コメントがある場所にスリープまたは何かを置いてテストします。

そして今大きなBUT:これは実際に機能しますが、phpはもともとマルチスレッド用に作成されていませんでした。スレッドセーフバージョンのphpが存在し、v5.4の時点ではバグはほとんどないようですが、マルチスレッド環境でphpを使用することは、phpマニュアルではまだ推奨されていません(ただし、マニュアルを更新しなかっただけかもしれません)これ、まだ)。より大きな問題は、多くの一般的な拡張機能がスレッドセーフではないことです。したがって、このphp拡張機能でスレッドを取得する可能性がありますが、依存している関数はまだスレッドセーフではないため、自分で記述していないコードで競合状態やデッドロックなどが発生する可能性があります...


5
それはひどく間違っています、あなたが参照した記事は2008年からです。PHPがコアでスレッドセーフでなければ、スレッド化されたSAPIモジュールがなかったでしょう。
Joe Watkins

1
@ジョー:わかりました、コアで変更しました。スレッドセーフですが、多くの拡張機能はそうではありません。
フランソワブルジョワ2013年

1
たくさん?あなたはそれが非常に少ないと思うでしょう、あなたはドキュメントを見つけましたがそれを正しく読むことができませんでした:注:*でマークされたものはスレッドセーフなライブラリではなく、マルチでサーバーモジュールとしてPHPで使用すべきではありません-スレッド化されたWindows Webサーバー(IIS、Netscape)。これはUnix環境ではまだ問題ではありません。
Joe Watkins、2013年

6
PHPは非常にスレッドセーフであり、何年もの間、一部の外部ライブラリといくつかのバンドルされたライブラリはそうではありませんが、十分に文書化されており、いずれにせよ明らかです。pthreadは、マルチスレッドのsapiでzendによって作成されたスレッドと同じくらい安全なスレッドを作成します。これは、私が単独でpthreadを作成したためです。サーバーAPIと同じように、PHPによって公開されているすべての利用可能なAPIを使用します。完全に安定しているとは言いませんが、描いた画像はまったく間違っており、十分な情報がありません。
Joe Watkins

@Joe:マニュアルで、これがUnix環境では問題ではないと記載されている場合、Unixシステムではapacheが複数のプロセスを使用し、Windowsではスレッドを使用するという事実を参照しています。したがって、基本的に彼らは「とにかくスレッドを使用していないのであれば、スレッドセーフでない拡張機能について心配する必要はない」と言っています。pthreadでスレッドを使用する場合、それはもちろん、UNIX環境でも問題になります。
フランソワブルジョワ2013年

17

pcntl_fork()を使用して、スレッドに似たものを実現できます。技術的にはそれは別個のプロセスであるため、2つの間の通信はスレッドでは単純ではなく、PHPがApacheによって呼び出された場合は機能しないと思います。


4
pcntl_forkを使用して、かなり大規模なデータインポートタスクを並列化しています。うまく動作し、約1時間で動作しました。学習曲線は少しありますが、何が起こっているかを理解すれば、それはかなり簡単です。
フランクファーマー

フランク、それはCLI phpとApache PHPのどちらですか?
Artem Russakovskii 09/09/15

@Artem:私も知りたいです。
Josh K

5
@Frank Farmerが私たちをからかっています... PECLパッケージのように。
ロジャー

1
CLIでpcntl_forkを使用していました。私はこれをApacheで試したことはありません。それは危険に思えます。CLIでも、いくつかの予期しない問題がありました。ある子がデータベースハンドルを閉じると(作業が終了したため)、兄弟の接続も閉じてしまうという問題が発生したようです。子供は親のコピーなので、奇妙なことに備えます。それ以来、exec()を使用して、完全に分離された新しいプロセスを単純に生成するようにコードを再設計しました。
フランクファーマー


7

pcntl_fork()あなたが探しているものですが、そのプロセスはスレッドではありません。したがって、データ交換の問題が発生します。それらを解決するには、phpのセマフォ関数(http://www.php.net/manual/de/ref.sem.php)を使用できますメッセージキューは、共有メモリセグメントよりも最初は少し簡単かもしれません。

とにかく、私が開発しているWebフレームワークで使用している戦略で、Webページのリソースを大量に消費するブロックを(おそらく外部要求で)並列に読み込みます。ジョブキューを実行して、待機しているデータを確認してからフォークします。すべてのプロセスのジョブをオフにします。完了すると、親プロセスがアクセスできる一意のキーの下にあるapcキャッシュにデータを保存します。すべてのデータが存在すると、それは継続します。usleep()apacheではプロセス間通信ができないため、待機に単純を使用しています(子供は親との接続を失い、ゾンビになります...)。だから、これは私に最後のことをもたらします:すべての子供を自殺することが重要です!プロセスをフォークするがデータを保持するクラスもあります。私はそれらを調べませんでしたが、zendフレームワークには1つあり、通常は低速ですが確実にコードを実行します。あなたはそれをここで見つけることができます: http://zendframework.com/manual/1.9/en/zendx.console.process.unix.overview.html 彼らはshmセグメントを使用していると思います!最後に、このZend Webサイトにエラーがあり、例に小さな間違いがあります。

while ($process1->isRunning() && $process2->isRunning()) {
    sleep(1);
}
should of course be:
while ($process1->isRunning() || $process2->isRunning()) {
    sleep(1);
}



5

PHPスレッディングクラスがあり、2年以上本番環境で問題なく実行されています。

編集:これは作曲家ライブラリとして、そして私のMVCフレームワークであるHazaar MVCの一部として利用できるようになりました。

参照:https : //git.hazaarlabs.com/hazaar/hazaar-thread


たとえば、例としてfile.phpのプログラムが、たとえば10kのWebサイトのURIのリストの存在を確認し、その結果をCSVファイルに保存する必要があるとします。このファイルは、問題?
ロジャー

サブプロセスは、web-server / parentスクリプトと同じユーザーとして実行されます。したがって、ファイルを書き込むときは、アクセス許可に関して通常と同じ考慮事項があります。ファイルの書き込みに問題がある場合は、/ tmpに書き込んでみて、それが機能しているときにそこから進んでください。
ジェイミーカール

1
リンクはあなたがここにウェイバックマシン上でそれを得ることができ、今のデザイン変更が原因死んでいる:web.archive.org/web/20130922043615/http://dev.funkynerd.com/...
トニー・

MVCフレームワークに追加されました。参照:git.hazaarlabs.com/hazaar/hazaar-thread
Jamie Carl


1

appserverテクディビジョンから聞いたことがありますか?

それはphpで書かれており、高トラフィックのphpアプリケーションのマルチスレッドを管理するappserverとして機能します。まだベータ版ですが、非常に有望です。


-3

ティックと呼ばれるややあいまいで、まもなく廃止される機能があります。私がこれまで使用した唯一のことは、スクリプトがSIGKILL(Ctrl + C)をキャプチャして正常に終了できるようにすることです。


3
ティックは並行して実行されません。基本的に、すべてのステートメントの後、tick関数が実行されます。tick関数が実行されている間、メインコードは実行されません。
フランクファーマー

1
ティックは、signal()ハンドラーにのみ必要です。
ニック・
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.