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


414

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

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


1
ここに私の質問と回答を参照してください。stackoverflow.com/questions/2101640/...
powtac


どのようにpthreadsの拡張機能を使用するには:phplobby.com/php-multi-thread-on-windows-pthreads-configuration
エムラMehmedov

興味があるかもしれません:pthreads.org
GibboK

現在2020年には、「pthread」の代わりに「並列」php.net/manual/en/intro.parallel.phpが必要なようです:stackoverflow.com/a/56451969/470749
Ryan

回答:


431

PHPでマルチスレッドが可能

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

PHPのドキュメントから:

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

警告: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

2回目の実行

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);
}

3
@ Baba、Xamppサーバーでpthreadを構成およびインストールできません。それを手伝ってくれませんか?
Irfan 2013

4
ここでwindowsバイナリをダウンロードするwindows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba

17
それは素晴らしいです、私は何年もPHPに触れていませんでしたが、今ではマルチスレッド機能を持っています!
巡洋艦2014年

1
素敵でシンプル!参考までに、Azure Cloud Winサーバーにアプリを展開しています。基本的な1コア構成のみが選択されている場合、コアを追加しない限りマルチスレッドは利用できません。
ミラノ

3
注意してください:pthreads拡張機能の作者であるJoe Watkinsが開発を中止し、新しい並列拡張機能を採用しました:github.com/krakjoe/pthreads/issues/929
Anton Belonovich

42

どうして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]);
    }
}

4
私は上記のソリューションを使用していますが、問題なく動作します。これが、phpを使用して並列処理を行う最も簡単な方法だったと思います。
GodFather

7
@ e-info128が言ったように、この実装はプロセスをフォークします。つまり、別のプロセスで実行されており、プロセスリソースを共有しません。そうは言っても、手元のジョブがリソースを共有する必要がない場合でも、これは機能し、並列に実行されます。
Raffi

1
セッション変数を使用せずにpopenに変数をどのように渡しますか?
atwellpub

@atwellpubまさか、これらはリソースを共有しない個別のプロセスです。セッションでもIPCメカニズムは扱いにくい
Cholthi Paul Ttiopic

1
それらにデータを渡すには、引数とRedisサーバーも使用できます。
Amir Fo

21

スレッド化は標準の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);

4
前回チェックしたとき(数年前)phpは、2つのプロセスが同時にファイルベースのセッションストレージにアクセスすることを許可していませんでした。これはファイルをロックし、2番目のプロセスは最初のスクリプトが停止するまで待機する必要があります。CLIではなく、Webサーバー環境について話しています。
Alexei Tenitski、2011

5
set_time_limit(0);うわぁ!絶対に、これをしないでください。
Kafoso

@Kafoso Kafosoなんで?WebスクリプトプロセッサとしてのPHPに同意しますが、CLIを使用しないのはなぜですか?何か問題が発生した場合、Ctrl + CでCLIを終了できます...
sijanec

14

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

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

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

pcntl_forkでプロセスをフォークして、子のPIDを返すことができます。次に、posix_killを使用してそのPIDを破棄できます。

つまり、親プロセスを強制終了した場合、子プロセスにシグナルを送信して、死ぬように伝える必要があります。PHP自体がこれを認識しない場合は、それを管理する関数を登録し、pcntl_signalを使用してクリーンな終了を行うことができます。


1
その回答は現在、非常に古くなっています(これは11歳であることを考えると非常に公平です)。以下のpthreadを見てください。
Maciej Paprocki

@MaciejPaprocki pThreadがphp 7.4で廃止され、代わりにparallelを使用する
Airy



5

exec()を使用してコマンドラインスクリプト(コマンドラインphpなど)を実行できます。出力をファイルにパイプすると、スクリプトはコマンドが完了するまで待機しません。

私はphp CLI構文を完全に覚えることはできませんが、次のようなものが必要です。

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

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


4

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



3

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

双方向のスレッド間通信をサポートし、子スレッドを削除する(孤立を防止する)ための組み込み保護機能も備えています。


3

次のオプションがあります。

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


3

pcntl_forkはどうですか?

マニュアルページで例を確認してください:PHP pcntl_fork

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
    }

?>

2

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


1

Linuxサーバーを使用している場合は、

exec("nohup $php_path path/script.php > /dev/null 2>/dev/null &")

いくつかの引数を渡す必要がある場合

exec("nohup $php_path path/script.php $args > /dev/null 2>/dev/null &")

script.php

$args = $argv[1];

またはSymfony https://symfony.com/doc/current/components/process.htmlを使用し ます

$process = Process::fromShellCommandline("php ".base_path('script.php'));
$process->setTimeout(0);     
$process->disableOutput();     
$process->start();

-1

現在のコメントを書いている時点では、PHPスレッドについては知りません。私はここで自分で答えを探しましたが、1つの回避策は、Webサーバーから要求を受信するPHPプログラムが、出力定式全体を、出力、要求への答え、バイナリファイルを格納するコンソールアプリケーションに委任することです。また、コンソールアプリケーションを起動したPHPプログラムは、バイナリファイルをバイト単位で、受信した要求に対する応答として返します。コンソールアプリケーションは、OpenMPを使用するC ++プログラムを含む、適切なスレッド処理をサポートするものを含め、サーバー上で実行される任意のプログラミング言語で記述できます。

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

uname -a

そして、そのコンソールコマンドの出力をHTML出力に出力して、サーバーソフトウェアの正確なバージョンを確認します。次に、同じバージョンのソフトウェアをVirtualBoxインスタンスにインストールし、完全に自己完結型の、できれば静的なバイナリをコンパイル/アセンブルし、それらをサーバーにアップロードします。それ以降、PHPアプリケーションは、適切なマルチスレッド機能を備えたコンソールアプリケーションの役割でこれらのバイナリを使用できます。サーバー管理者が必要なすべてのプログラミング言語実装をサーバーにインストールしていない場合、これはダーティで信頼性が低く、回避策です。注意すべきことは、PHPアプリケーションがコンソールアプリケーションを受信するすべてのリクエストで、終了/終了/ get_killedすることです。

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


-3

私は何かを見逃したかもしれませんが、execは、私がWindowsで使用しているWindows環境では非同期として機能しませんでした。それは魅力のように機能しました;)

$script_exec = "c:/php/php.exe c:/path/my_ascyn_script.php";

pclose(popen("start /B ". $script_exec, "r"));

1
PHPでマルチスレッドをハックするには、複数のpopen()ステートメントを開く必要があります(PHPはそれが完了するまで待機しません)。次に、半ダースほど開いた後、それらに対してpclose()を呼び出します(PHPはpclose()が完了するまで待機します)。コードでは、各スレッドを開いた直後に閉じるため、コードはマルチスレッドのように動作しません。
Kmeixner 2014年

-5

マルチスレッドとは、複数のタスクまたはプロセスを同時に実行することを意味します。phpでこれを実現するには、次のコードを使用します。ただし、phpで直接マルチスレッドを実現する方法はありませんが、以下の方法でほぼ同じ結果を得ることができます。

chdir(dirname(__FILE__));  //if you want to run this file as cron job
 for ($i = 0; $i < 2; $i += 1){
 exec("php test_1.php $i > test.txt &");
 //this will execute test_1.php and will leave this process executing in the background and will go         

 //to next iteration of the loop immediately without waiting the completion of the script in the   

 //test_1.php , $i  is passed as argument .

}

Test_1.php

$conn=mysql_connect($host,$user,$pass);
$db=mysql_select_db($db);
$i = $argv[1];  //this is the argument passed from index.php file
for($j = 0;$j<5000; $j ++)
{
mysql_query("insert  into  test   set

                id='$i',

                comment='test',

                datetime=NOW() ");

}

これはtest_1.phpを2回同時に実行し、両方のプロセスが同時にバックグラウンドで実行されるため、この方法でphpでマルチスレッドを実現できます。

この人は本当に良い仕事をし ました


また、これはマルチスレッディングとは関係ありません。これは並列処理です。全く違うもの。
Digital Human

回避策としての私の意見では、緊急ハック、提供されたソリューションの背後にあるアイデアは非常に適切ですが、同時実行とハードウェアベースの違いがあるため、さまざまな人々が「真のマルチスレッド」を構成するものについて炎上する可能性があると思いますyoutube.com/watch?v=cN_DpYBzKso
Martin Vahi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.