web-dev-qa-db-ja.com

PHPアプリケーションでマルチスレッドを使用する方法

本当かどうかに関わらず、PHPにマルチスレッドモデルを実装する現実的な方法はありますか。しばらく前に、オペレーティングシステムにPHP実行可能ファイルの別のインスタンスをロードさせ、他の同時プロセスを処理させることをお勧めします。

この問題は、PHPコードの実行が終了したときにPHPインスタンスがメモリ内に残ることです。これは、PHP内からそれを強制終了する方法がないためです。あなたがいくつかのスレッドをシミュレートしているのであれば、あなたは何が起こるのか想像することができます。それで、私はまだマルチスレッドがPHPの中から効果的にされるか、またはシミュレートされることができる方法を探しています。何か案は?

366
Steve Obbayi

マルチスレッドはphpで可能です

はい pthreadsを使用してPHPでマルチスレッドを実行できます

PHPドキュメントから:

pthreadsは、PHPのマルチスレッドに必要なすべてのツールを提供するオブジェクト指向APIです。 PHPアプリケーションは、Thread、Workers、およびThreadオブジェクトを作成、読み取り、書き込み、実行、および同期することができます。

警告:pthreads拡張子はWebサーバー環境では使用できません。したがって、PHPのスレッド化は、CLIベースのアプリケーションにのみ適用されます。

簡単なテスト

#!/usr/bin/php
<?php
class AsyncOperation extends Thread {

    public function __construct($arg) {
        $this->arg = $arg;
    }

    public function run() {
        if ($this->arg) {
            $sleep = mt_Rand(1, 10);
            printf('%s: %s  -start -sleeps %d' . "\n", date("g:i:sa"), $this->arg, $sleep);
            sleep($sleep);
            printf('%s: %s  -finish' . "\n", date("g:i:sa"), $this->arg);
        }
    }
}

// Create a array
$stack = array();

//Initiate Multiple Thread
foreach ( range("A", "D") as $i ) {
    $stack[] = new AsyncOperation($i);
}

// Start The Threads
foreach ( $stack as $t ) {
    $t->start();
}

?>

ファーストラン

12:00:06pm:     A  -start -sleeps 5
12:00:06pm:     B  -start -sleeps 3
12:00:06pm:     C  -start -sleeps 10
12:00:06pm:     D  -start -sleeps 2
12:00:08pm:     D  -finish
12:00:09pm:     B  -finish
12:00:11pm:     A  -finish
12:00:16pm:     C  -finish

セカンドラン

12:01:36pm:     A  -start -sleeps 6
12:01:36pm:     B  -start -sleeps 1
12:01:36pm:     C  -start -sleeps 2
12:01:36pm:     D  -start -sleeps 1
12:01:37pm:     B  -finish
12:01:37pm:     D  -finish
12:01:38pm:     C  -finish
12:01:42pm:     A  -finish

実世界の例

error_reporting(E_ALL);
class AsyncWebRequest extends Thread {
    public $url;
    public $data;

    public function __construct($url) {
        $this->url = $url;
    }

    public function run() {
        if (($url = $this->url)) {
            /*
             * If a large amount of data is being requested, you might want to
             * fsockopen and read using usleep in between reads
             */
            $this->data = file_get_contents($url);
        } else
            printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
    }
}

$t = microtime(true);
$g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", Rand() * 10));
/* starting synchronization */
if ($g->start()) {
    printf("Request took %f seconds to start ", microtime(true) - $t);
    while ( $g->isRunning() ) {
        echo ".";
        usleep(100);
    }
    if ($g->join()) {
        printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data));
    } else
        printf(" and %f seconds to finish, request failed\n", microtime(true) - $t);
}
391
Baba

なぜあなたは popen を使わないのですか?

for ($i=0; $i<10; $i++) {
    // open ten processes
    for ($j=0; $j<10; $j++) {
        $pipe[$j] = popen('script2.php', 'w');
    }

    // wait for them to finish
    for ($j=0; $j<10; ++$j) {
        pclose($pipe[$j]);
    }
}
34
masterb

スレッド化は在庫のあるPHPでは利用できませんが、HTTP要求を非同期呼び出しとして使用することで並行プログラミングが可能です。

Curlのタイムアウト設定を1に設定し、互いに関連付けたいプロセスに同じsession_idを使用すると、以下の例のようにセッション変数と通信できます。この方法ではブラウザを閉じることさえでき、並行プロセスはまだサーバー上に存在します。

このように正しいセッションIDを確認することを忘れないでください。

http://localhost/test/verifysession.php?sessionid = [ 正しいID]

startprocess.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo $_REQUEST["PHPSESSID"];

process1.php

set_time_limit(0);

if ($_REQUEST["sessionid"])
   session_id($_REQUEST["sessionid"]);

function checkclose()
{
   global $_SESSION;
   if ($_SESSION["closesession"])
   {
       unset($_SESSION["closesession"]);
       die();
   }
}

while(!$close)
{
   session_start();
   $_SESSION["test"] = Rand();
   checkclose();
   session_write_close();
   sleep(5);
}

verifysession.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
var_dump($_SESSION);

closeprocess.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
$_SESSION["closesession"] = true;
var_dump($_SESSION);
18
Ricardo

スレッド化することはできませんが、phpではある程度のプロセス制御があります。ここで役立つ2つの機能セットは次のとおりです。

プロセス制御機能 http://www.php.net/manual/en/ref.pcntl.php

POSIX関数 http://www.php.net/manual/en/ref.posix.php

あなたはあなたのプロセスをpcntl_forkで分岐することができます - 子のPIDを返します。それから、そのPIDを破棄するためにposix_killを使うことができます。

そうは言っても、もしあなたが親プロセスを殺したら、それが死ぬように言うシグナルが子プロセスに送られるべきです。 php自体がこれを認識していない場合は、それを管理する関数を登録し、pcntl_signalを使用してクリーンな終了をすることができます。

11

私はこれが古い質問であることを知っています、しかし検索している人々のために、PHPマルチスレッド機能を与えるCで書かれたPECL拡張があります、それはここにあります https:/ /github.com/krakjoe/pthreads

8
JasonDavis

スレッドを使用することはpthreads PECL拡張によって可能になります

http://www.php.net/manual/ja/book.pthreads.php

8
pinkal vansia

あなたはスレッドをシミュレートすることができます。 PHPはpopen(またはproc_open)でバックグラウンドプロセスを実行できます。これらのプロセスは、stdinとstdoutを介して通信できます。もちろん、これらのプロセスはそれ自体phpプログラムになることができます。それはおそらくあなたが得ると同じくらい近いです。

5
Pete

Exec()を使用してコマンドラインスクリプト(コマンドラインphpなど)を実行できます。出力をファイルにパイプ処理しても、スクリプトはコマンドが終了するのを待ちません。

Php CLIの構文はよく覚えていませんが、次のようなものがほしいと思います。

exec("/path/to/php -f '/path/to/file.php' | '/path/to/output.txt'");

セキュリティ上の理由から、かなりの数の共有ホスティングサーバーでexec()がデフォルトで無効になっていると思いますが、試してみる価値があります。

5
Adam Hopkinson

Pcntl_forkはどうですか。

例については私たちのマニュアルページをチェックしてください: PHP pcntl_fork

3
Jarrod

あなたがやろうとしていることに応じて、あなたはそれを達成するためにcurl_multiを使うこともできます。

3
Sheldmandu

私はこれが古い方法であることを知っています、しかしあなたは http://phpthreadlib.sourceforge.net/ を見ることができます

双方向のスレッド間通信をサポートし、子スレッドを強制終了する(孤児を防ぐ)ための保護機能も組み込まれています。

3
Unsigned

pcntl_forkセーフモードがオンになっているとWebサーバー環境では動作しません。この場合、それはPHPのCLIバージョンでしか動作しません。

2
Stilero

次の選択肢があります。

  1. multi_curl
  2. 同じシステムコマンドを使用することができます
  3. 理想的なシナリオは、C言語でスレッド関数を作成し、PHPでコンパイル/構成することです。今その機能はPHPの機能になります。
2
Manoj Donga

Threadクラス はPECL pthreads≧2.0.0以降で利用可能です。

2

私の現在のコメントの執筆時点では、私はPHPスレッドについて知りません。私は自分自身で答えを探すようになりましたが、1つの回避策はWebサーバーから要求を受け取るPHPプログラムがその出力を保存するコンソールアプリケーションに全体の答えの定式化を委任することです。バイナリファイルとコンソールアプリケーションを起動したPHPプログラムにリクエストを送信すると、受信したリクエストに対する応答として、そのバイナリファイルがバイト単位で返されます。コンソールアプリケーションは、OpenMPを使用するC++プログラムなど、適切なスレッドをサポートしているものを含め、サーバー上で動作する任意のプログラミング言語で作成できます。

信頼できない、汚い、トリックの1つは、コンソールアプリケーションの実行にPHPを使用することです。 "uname"、

uname -a

そのコンソールコマンドの出力をHTML出力に出力して、サーバーソフトウェアの正確なバージョンを調べます。次に、まったく同じバージョンのソフトウェアをVirtualBoxインスタンスにインストールし、完全に独立した、できれば静的なバイナリをコンパイル/アセンブルしてから、それらをサーバーにアップロードします。それ以降、PHPアプリケーションは、適切なマルチスレッドを持つコンソールアプリケーションの役割でこれらのバイナリを使用できます。サーバー管理者が必要なプログラミング言語の実装をすべてサーバーにインストールしていない場合、これは汚い、信頼できない状況への回避策です。注意が必要なのは、PHPアプリケーションがコンソールアプリケーションを受信する要求ごとに/ exit/get_killedが終了することです。

ホスティングサービス管理者がそのようなサーバー使用パターンについてどう思うかということに関して、私はそれが文化に帰結すると思います。北ヨーロッパでは、サービスプロバイダは何を通知し、コンソールコマンドの実行が許可され、マルウェア以外のファイルのアップロードが許可され、サービスプロバイダは数分後または30秒後にもサーバープロセスを強制終了する権利があります。それから、ホスティングサービス管理者は、適切な苦情を申し立てるための議論を欠いている。米国と西ヨーロッパでは状況や文化が非常に異なっているので、米国や西ヨーロッパではホスティングサービスプロバイダが上記のトリックを使用するホスティングサービスクライアントへのサービス提供を拒否する可能性が非常に高いと思います。米国のホスティングサービスに関する私の個人的な経験と、西ヨーロッパのホスティングサービスについて他の人々から聞いたことを考えると、それは私の推測です。私の現在のコメント(2018_09_01)を書いている時点で、私は南ヨーロッパのホスティングサービスプロバイダ、南ヨーロッパのネットワーク管理者の文化的規範について何も知りません。

0
Martin Vahi