pthreads

はじめに

pthreads はオブジェクト指向の API で、PHP でマルチスレッド処理を行うのに必要なすべてのツールを提供します。 PHP アプリケーションで、Thread や Worker そして Threaded を作ったり読み書きしたり実行したりできるようになります。

警告

この拡張モジュールはメンテナンスされておらず、終了していると考えられます。

ヒント

parallel を代わりに使うことも検討してください。

警告

pthreads 拡張モジュールは、Webサーバー環境では使えません。 よって、PHP でのスレッド処理は CLIベースのアプリケーションに限られています。

警告

ptheads (v3) は PHP 7.2+ で使えます。 これは ZTS モードが 7.0 と 7.1 では安全でないためです。

Threaded クラスが、pthreads が実行できる機能のもとになります。 このクラスは同期のメソッドとプログラマにとって便利なインタフェイスをいくつか公開しています。

Thread クラスと使うと、 それを継承し、run メソッドを実装するだけでスレッドを作れます。 あらゆるメンバにスレッドの参照を使ってあらゆるコンテキストで読み書きができます。 また、あらゆるコンテキストが public と protected なメソッドを実行できます。 run メソッドは、この実装オブジェクトが作られたスレッドとは別の、 Thread::start() が呼ばれたスレッドで実行されます。 スレッドを作ったコンテキストだけが、スレッドの start や join を行えます。

Worker クラスは永続的な状態を保持し、 Thread::start() (またはそれを継承したメソッド) を呼んだ時点から オブジェクトがスコープ外に消えるまで、 あるいは明示的に(Worker::shutdown()経由で) shutdown するまで利用できます。 このオブジェクトを参照する任意のコンテキストから、 タスクを(Worker::stack() 経由で)ワーカーに積むことができ、 これを、Worker が別スレッドで実行します。 Worker の run メソッドは、あらゆるオブジェクトがスタックに積まれる前に実行されます。 そのため、オブジェクトで必要となるリソースの初期化に使えます。

Pool クラスを使って、 Threaded オブジェクトを分散させるワーカークラスのグループを作ることができます。 複数スレッドを扱う際に最も簡単かつ効率的な方法です。

警告

Pool クラスは Threaded クラスを継承していません。 そして、Pool ベースのオブジェクトは通常の PHP オブジェクトです。 よって、そのインスタンスをコンテキスト間で共有してはいけません。

Volatile は pthreads v3 で登場した新しいクラスです。 これは Threaded クラスのプロパティを変更可能にするために使います (なぜなら、これらはデフォルトでは変更できないからです) PHP の配列を Threaded コンテキストに保存する目的でも使えます。

同期はスレッドを扱う際に重要な機能です。 pthreads が作る全てのオブジェクトには (Java プログラマーならおなじみの) Threaded::wait()Threaded::notify() による同期処理が組み込まれています。 あるオブジェクトの Threaded::wait() を呼ぶと、 別のコンテキストから同じオブジェクトの Threaded::notify() が呼ばれるのを待つようになります。 これを使えば、PHP 内のスレッド化されたオブジェクト (Threaded Object) どうしで強力な同期処理ができるようになります。

警告

マルチスレッドで実行されるあらゆるオブジェクトは Threaded を継承すべきです。

データストレージ: 目安として、シリアライズ可能なデータ型なら何でも、スレッド化されたオブジェクトのメンバーとして使えます。 そのオブジェクトへの参照を持つあらゆるコンテキストから、メンバーの読み書きができます。 すべてのデータ型がシリアライズされるわけではなく、基本型はそのままの形式で格納されます。 それ以外の複雑な型や配列、スレッド化されていないオブジェクトは、シリアライズして格納されます。 これらは、参照を持つ任意のコンテキストから、スレッド化されたオブジェクトへの読み書きができます。 スレッド化されたオブジェクトを例外として、 あるスレッド化されたオブジェクトのメンバーを設定するために使うあらゆる参照は、 そのスレッド化されたオブジェクト内の参照とは区別されます。 同じデータを、いつでもどのコンテキストからでも、 スレッド化されたオブジェクトから直接読み込めます。

static メンバー: 新しいコンテキスト (Thread あるいは Worker) を作るときには、一般的にそれらはコピーされます。 しかし、リソースおよび内部状態を持つオブジェクトは、安全性を考慮して null 化されます。 これを、一種のスレッドローカルストレージとして使えます。 たとえば、データベースサーバーへの接続情報と接続そのものをstatic なメンバーとして持つクラスがあるとします。 コンテキストを開始するときには接続情報だけがコピーされ、接続自体はコピーされません。 新しいコンテキスト上ではそのコンテキストを作ったオブジェクトと同じ方法で接続を立ち上げることができ、 その接続を同じ場所に格納しても元のコンテキストには何も影響を及ぼしません。

警告

print_r や var_dump などのオブジェクトのデバッグ用関数を実行するきには、 再帰の制限が含まれません。

注意: リソース: PHP のリソースを定義している拡張モジュールは、この手の環境で扱うには不十分です。 pthreads はリソースをコンテキスト間で共有するための対策を用意していますが、 大半のリソース型は安全には扱えません。 コンテキスト間でリソースを共有するときには、いくら注意してもしすぎることはありません。

警告

pthreads を実行する環境下では、安定した環境を提供するためにいくつかの制約と限界があります。

add a note

User Contributed Notes 4 notes

up
27
anonymous at example dot com
8 years ago
Here are some notes regarding PHP pThreads v3 that I have gathered:
-namespace: It does not understand namespaces. 
-globals: It won't serialize GLOBALS at all! And will not register new ones.
-classes: It registers new classes okay.
-functions: Will not register ANY functions - they must all be in static classes. It does understand PHP native functions. 
-consts: previous constants will transfer over. Don't make any new ones thou!
-pThreads only work in CLI - of course!
-If a thread crashes it automatically gets recreated.
-In order to 'force kill' any thread the parent must be killed. Or wait until all other tasks queued are complete and then terminate.
-Anything registered in a pThread does not seem to join the main thread ... which is good!
-pThreads seem to be very powerful on multi-core environments, just need to be careful on system resources... it can and will lock up a system if mis-configured.
-Finally, finding help for PHP pThreads is slim to none... especially v3!

Good luck!
up
9
meadowsjared at gmail dot com
5 years ago
In this example, it shows how to use a threaded with a pool to get an array of results, using pThreads v3.2.1 and php 7.3.23

<?php
class TestWork extends Threaded {
//updated version that works with pThreads v3.2.1 and php 7.3.23
    protected $complete;
    //$pData is the data sent to your worker thread to do it's job.
    public function __construct($pData) {
        //transfer all the variables to local variables
        $this->complete = false;
        $this->testData = $pData;
    }
    //This is where all of your work will be done.
    public function run() {
        usleep(2000000); //sleep 2 seconds to simulate a large job
        $this->complete = true;
    }
    public function isDone() {
        return $this->complete;
    }
}
class ExamplePool extends Pool {
    public $data = array(); // used to return data after we're done
    private $numTasks = 0; // counter used to know when we're done
    /**
     * override the submit function from the parent
     * to keep track of our jobs
     */
    public function submit(Threaded $task) {
        $this->numTasks++;
        parent::submit($task);
    }
    /**
     * used to wait until all workers are done
     */
    public function process() {
        // Run this loop as long as we have
        // jobs in the pool
        while (count($this->data) < $this->numTasks) {
            $this->collect(function (TestWork $task) {
                // If a task was marked as done
                // collect its results
                if ($task->isDone()) {
                    $tmpObj = new stdclass();
                    $tmpObj->complete = $task->complete;
                    //this is how you get your completed data back out [accessed by $pool->process()]
                    $this->data[] = $tmpObj;
                }
                return $task->isDone();
            });
        }
        // All jobs are done
        // we can shutdown the pool
        $this->shutdown();
        return $this->data;
    }
}
$pool = new ExamplePool(3);
$testData = 'asdf';
for($i=0;$i<5;$i++) {
    $pool->submit(new TestWork($testData));
}
$retArr = $pool->process(); //get all of the results
echo '<pre>';
print_r($retArr); //return the array of results (and maybe errors)
echo '</pre>';
?>
up
9
jasonrlester at yahoo dot com
12 years ago
Note that this extension *is* a high level implementation of POSIX threads, including on Windows (which is why pthreadsV*.dll is required)
up
6
admin at deosnet dot com
11 years ago
Hello,

WARNING : When using Stackable objects in callable functions by your Threads, you must be very careful if you use it as an array. Indeed, if you do not copy your Stackable "array" in a local variable, the execution time can drop drastically !

Also, if you want to modify an array() in a function, you will also store in a local variable in order that your array is in a thread-safe context.
To Top