はじめに
PHPは、プログラムを上から順に実行するシングルスレッドの言語です。
今回は、**『parallelという拡張モジュールを使うことで、PHPでマルチスレッドを扱える!』**そうなので、試したいと思います💡
parallelとは
- PHP拡張モジュール
- PHP 7.2以降で動作
- PHP ZTS版でのみ動作
- Webからのリクエストにも対応
- 導入が少々手間
※マルチスレッドと言えばpthreadsが使われているそうですが、「この拡張機能はメンテナンスされておらず、終了していると考えられます。」と書かれています。PHP 7.4以降は、サポートしていないということみたい。。
準備
使用技術
- Docker
ディレクトリ構成
.
├── Dockerfile
└── src
└── index.php
src
の中をホストと共有するような構成にしています。
コンテナ作成
FROM php:7.4-zts
RUN apt-get update
RUN pecl install parallel \
&& docker-php-ext-enable parallel
WORKDIR /var/www/html
Docker Hubにイメージをプッシュしてあるので、詳しくはそちらをご参照ください。
-
zts
は、「ZTS (Zend Thread Safety) 」のことで、要はマルチスレッドを安全に扱うモードです。 -
pecl
コマンドを使って、parallelをインストールしています。PECLは、「PHPの拡張モジュールを提供してくれるサービス」です。 -
parallel
を有効化しています。
CLI上で実行
$ docker build --no-cache=true -t my_php:latest .
上記のDockerfileからイメージを作成します。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my_php latest ff3880c9f1c9 41 seconds ago 407MB
イメージが作成されているか確認できました。
docker run -itd --rm -v $PWD/src:/var/www/html/ --name my_php my_php:latest /bin/bash
dockerのコンテナを作成することができます。
$ docker exec -it my_php bash
root@0e826bad2d7d:/var/www/html# php -v
PHP 7.4.15 (cli) (built: Feb 4 2021 17:37:54) ( ZTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
root@0e826bad2d7d:/var/www/html# php -m
〜〜略〜〜
parallel
〜〜〜〜〜
root@0e826bad2d7d:/var/www/html# pecl list -a
〜〜略〜〜
Installed packages, channel pecl.php.net:
=========================================
Package Version State
parallel 1.1.4 stable
コンテナの中に、コマンドを実行してみました。ちゃんと、parallelが入ってることを確認できました🥳
マルチスレッドのコード
サンプル1
<?php
$runtime = new \parallel\Runtime();
$runtime->run(function(){
echo '★';
});
for ($i = 0; $i < 100; $i++) {
echo '.';
}
$runtime->run()
で新たなプロセスが実行されます。
root@0e826bad2d7d:/var/www/html# php index.php
..................................................★..................................................
root@0e826bad2d7d:/var/www/html# php index.php
...........................................................★.........................................
root@0e826bad2d7d:/var/www/html# php index.php
.....................★...............................................................................
root@0e826bad2d7d:/var/www/html# php index.php
...........★.........................................................................................
root@0e826bad2d7d:/var/www/html# php index.php
....................................................................................................★
★
が出力されるタイミングがバラバラです。つまり、メインのスレッドとは別のスレッドを立ち上がり、出力していることを意味します。
サンプル2
<?php
$runtime = new \parallel\Runtime();
$runtime->run(function(){
sleep(1);
echo 'サブ', PHP_EOL;
});
echo 'メイン', PHP_EOL;
root@0e826bad2d7d:/var/www/html# php index.php
メイン
サブ
1秒経ってから、「サブ」が出力されます。このことからもメインルーチンとは別のスレッドで動いていることがわかります。
Webからの実行
- Docker Compose
ディレクトリ構成
.
├── docker-compose.yml
├── nginx
│ └── default.conf
├── php
│ └── Dockerfile
└── src
└── index.php
php
FROM mohsenmottaghi/php-fpm-zts:7.4
RUN pecl install parallel \
&& docker-php-ext-enable parallel
WORKDIR /var/www/html
先程とは、違うベースイメージを使用しています。
nginx
server {
listen 80;
root /var/www/html;
index index.php index.html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
コード
<?php
$runtime = new \parallel\Runtime();
$runtime->run(function(){
echo '★';
});
for ($i = 0; $i < 10000; $i++) {
if ($i % 200 === 0) echo "\n";
echo '.';
}
docker-compose up -d
を実行し、localhost:8000
にアクセスすると確認できます。
終わりに
もっと色々なことができるそうですが、まずはお試しでということで😌
pthreadsとは違い、Webからのリクエストでも動作するので、今後の発展に期待ですね!!!
ps. @sj-i さん、コメントいただきありがとうございました!