PHP7.4でもマルチスレッド!!


以前「PHP7.4からのマルチスレッド」という記事を書きましたが、内容が薄すぎたのでもう少し紹介していきたいと思います。コードは全く載せていないのでサンプルはParallelのテストコードを見てください。

なお、 PHPにおけるマルチスレッドはCLI限定となります。(CLI限定なのはpthreadsだけでした) マルチプロセスを実現するpcntlではwebサーバで使用することを非推奨としていますが、pthreadsやparallelではコンパイルから拒否されます。

拡張モジュール

krakjoe氏が開発しているParallelを使用します。
https://github.com/krakjoe/parallel

マルチスレッドモデル

ParallelはGo言語のgoroutineに大きく影響を受けています。
https://www.php.net/manual/ja/philosophy.parallel.php

parallel\Runtime

RuntimeはpthreadsのWorkerにかなり近いです。
インスタンスが生成されたときにスレッドが起動し、破棄されるときまたはclose()やkill()が呼ばれたときにスレッドをシャットダウンします。run()を呼ぶと引数で指定したクロージャがスケジュールされ、即座にFutureを返します。スケジュールされたクロージャは順次実行されます。

parallel\Future

何の変哲もないFutureパターンの実装です。
cancel()によりスケジュールされたタスクを中断させることができます。

parallel\Channel

仕様はgoroutineのチャンネルとほぼ同じです。Parallelではチャンネルに文字列で名前を設定することができます。匿名でも勝手に名前が付きます。open()によって好きな場所からチャンネル名を指定して特定のチャンネルを得ることができます。また、バッファのサイズを無制限にできます。PHPっぽさが感じられますね。

parallel\Events

複数のFutureやChannelを束ねてデータの受信をイベントとするイベントストリームを提供します。また、poll()でイベントを受け取れますが、データの送信元となったFutureやChannelはイベントストリームから削除されます。継続して同じChannelからのイベントを受け取りたい場合、Event\Type::ReadであろうとEvent\Type::WriteであろうとaddChannel()によって再登録が必要です。

parallel\Events\Input

複数のチャンネルにそれぞれ指定したデータを送信する操作を定義することができます。1つのチャンネルに複数のデータを送信することはできません。Eventsにセットすることで、ストリームが動き始めたタイミングで各チャンネルにデータを送信され、Event\Type::Writeのイベントが発生します。また、Inputに設定した項目は送信されたものから削除されていきます。

parallel\Sync

昔ながらの低レベルの実装をしたければこれを使いましょう。ほとんどのケースではChannelの方が優れているとのこと。

ParallelではCLIは要求されないらしい

pthreadsでもParallelでもコンパイル時にZTSは要求されます。
pthreads: https://github.com/krakjoe/pthreads/blob/master/config.m4#L11-L16
Parallel: https://github.com/krakjoe/parallel/blob/develop/config.m4#L14-L19

pthreadsでは実行時(PHP_MINIT_FUNCTION)にcli, phpdbg, homegearのいずれかであることをチェックします。
https://github.com/krakjoe/pthreads/blob/master/php_pthreads.c#L93-L113
https://github.com/krakjoe/pthreads/blob/master/php_pthreads.c#L251-L255

Parallelではそういった制限がありません。