https://github.com/tpunt/pht
A new threading extension for PHP
https://github.com/tpunt/pht
concurrency multithreading parallelism php php-extension pthreads threading
Last synced: 5 months ago
JSON representation
A new threading extension for PHP
- Host: GitHub
- URL: https://github.com/tpunt/pht
- Owner: tpunt
- License: bsd-3-clause
- Created: 2017-12-21T20:55:50.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2018-04-11T13:33:52.000Z (over 7 years ago)
- Last Synced: 2025-03-28T15:51:49.797Z (9 months ago)
- Topics: concurrency, multithreading, parallelism, php, php-extension, pthreads, threading
- Language: C
- Homepage:
- Size: 188 KB
- Stars: 179
- Watchers: 10
- Forks: 14
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# The Pht Threading Extension
This extension exposes a new approach to threading in PHP.
Quick feature list:
- Classes, functions, and files may be threaded
- The inter-thread communication (ITC) data structures include: hash table, queue, vector
- Threads are reusable for any number of tasks
Requirements:
- A ZTS version of PHP 7.2. The master branch of php-src is not currently compatible
Any Unix-based OS is supported (including OS X), along with Windows. This extension was explicitly tested on OS X (Yosemite and Sierra), Ubuntu 14.04 (32bit), and Windows Server 2012 (the pthreads-win32 library is needed).
Documentation: [php.net/pht](http://php.net/pht)
Contents:
- [Installation](https://github.com/tpunt/pht#installation)
- [Pthreads VS pht](https://github.com/tpunt/pht#pthreads-vs-pht)
- [The Basics](https://github.com/tpunt/pht#the-basics)
- [API](https://github.com/tpunt/pht#api)
- [Quick Examples](https://github.com/tpunt/pht#quick-examples)
- [Threading Types](https://github.com/tpunt/pht#threading-types)
- [Class Threading](https://github.com/tpunt/pht#class-threading)
- [Function Threading](https://github.com/tpunt/pht#function-threading)
- [File Threading](https://github.com/tpunt/pht#file-threading)
- [Inter-Thread Communication Data Structures](https://github.com/tpunt/pht#inter-thread-communication-data-structures)
- [Queue](https://github.com/tpunt/pht#queue)
- [Vector](https://github.com/tpunt/pht#vector)
- [Hash Table](https://github.com/tpunt/pht#hash-table)
- [Atomic Values](https://github.com/tpunt/pht#atomic-values)
- [Atomic Integer](https://github.com/tpunt/pht#atomic-integer)
This extension was built using a few ideas from the [pthreads](https://github.com/krakjoe/pthreads) extension. I'd therefore like to give credit to, as well as thank, [Joe Watkins](https://github.com/krakjoe) for his great work on the pthreads project!
## Installation
If you're using a Unix-based OS, then you can build the extension from source by:
```php
git clone https://github.com/tpunt/pht
cd pht
git checkout tags/v0.0.1
phpize
./configure
make
make install
```
If you're using Windows, see the [release page](https://github.com/tpunt/pht/releases) for the appropriate .dll extension file. The pthreadVC2.dll file (distributed alongside the extension's .dll file) will need to be made available in your `PATH` environment variable.
Once the .so or .dll file has been acquire, the php.ini file will then need to be updated (to load the extension) with:
```
extension="path/to/pht_file"
```
## Pthreads vs pht
Both extensions have their own advantages and disadvantages.
Pthreads advantages over pht:
- Uses synchronised blocks over mutex locks (which can be cleaner to use and harder to mess up)
- Easier communication between threads (no knowledge of data structures is required, which PHP developers tend to lack in)
- Maturer
Pht advantages over pthreads:
- No serialisation of properties on threaded objects, meaning:
- No performance hit when reading from or writing to properties
- No unfamiliar semantics, such as implicitly casting arrays to `Volatile` objects or property immutability for some types
- No limitations on what types of data can be stored as properties on threaded objects
- The ability to thread functions and files (as well as classes)
- Threaded classes implement a `Runnable` interface, rather than having to inherit (so more flexibility on inheritance)
## The Basics
This approach to threading abstracts away the thread itself behind a dedicated object (`Thread`), where tasks are added to that thread's internal task queue.
Example:
```php
addClassTask(Task::class);
// Thread::addFunctionTask(callable $fn, mixed ...$fnArgs) : void;
$thread->addFunctionTask(function ($zero) {var_dump($zero);}, 0);
// Thread::addFileTask(string $filename, mixed ...$globals) : void;
$thread->addFileTask('some_file.php', 1, 2, 3);
$thread->start();
$thread->join();
```
`some_file.php`:
```php
one = $one;
}
public function run()
{
var_dump($this->one);
}
}
$thread = new Thread();
$thread->addClassTask(Task::class, 1);
$thread->start();
$thread->join();
```
All `$ctorArgs` being passed into the `Thread::addClassTask()` method will be serialised.
#### Function Threading
Functions must not refer to `$this` (it will become `null` in the threaded context), and must not import variables from their outer scope (via the `use` statement). They should be completely standalone.
```php
addFunctionTask(static function($one) {var_dump($one);}, 1);
$thread->addFunctionTask(function() {var_dump(2);});
$thread->addFunctionTask('aFunc');
$thread->addFunctionTask('array_map', function ($n) {var_dump($n);}, [4]);
$thread->addFunctionTask(['Test', 'run']);
$thread->addFunctionTask([new Test, 'run2']);
$thread->start();
$thread->join();
```
All `$fnArgs` being passed into the `Thread::addFunctionTask()` method will be serialised.
#### File Threading
To pass data to the file being threaded, pass them as additional arguments to the `Thread::addFileTask()` method. They will then become available in a special `$_THREAD` superglobals array inside of the threaded file.
```php
addFileTask('file.php', 1, 2, 3);
$thread->start();
$thread->join();
```
`file.php`
```php
addFunctionTask(function ($queue, $queueItemCount) {
for ($i = 0; $i < $queueItemCount; ++$i) {
$queue->lock();
$queue->push($i);
$queue->unlock();
}
}, $queue, $queueItemCount);
$thread->start();
while (true) {
$queue->lock();
if ($queue->size() === $queueItemCount) {
$queue->unlock();
break;
}
$queue->unlock();
}
$thread->join();
// since we are no longer using $queue in multiple threads, we don't need to lock it
while ($queue->size()) {
var_dump($queue->pop());
}
```
#### Vector
```php
addFunctionTask(function ($vector, $i) {
$vector->lock();
$vector->push($i);
$vector->unlock();
}, $vector, $i);
}
$thread->start();
while (true) {
$vector->lock();
if ($vector->size() === $vectorItemCount) {
$vector->unlock();
break;
}
$vector->unlock();
}
$thread->join();
// since we are no longer using $vector in multiple threads, we don't need to lock it
for ($i = 0; $i < $vectorItemCount; ++$i) {
var_dump($vector[$i]);
}
```
#### Hash Table
```php
addFunctionTask(function ($hashTable, $i) {
$hashTable->lock();
$hashTable[chr(ord('a') + $i)] = $i;
$hashTable->unlock();
}, $hashTable, $i);
}
$thread->start();
while (true) {
$hashTable->lock();
if ($hashTable->size() === $hashTableItemCount) {
$hashTable->unlock();
break;
}
$hashTable->unlock();
}
$thread->join();
// since we are no longer using $hashTable in multiple threads, we don't need to lock it
for ($i = 0; $i < $hashTableItemCount; ++$i) {
var_dump($hashTable[chr(ord('a') + $i)]);
}
```
### Atomic Values
Atomic values are classes that wrap simple values. These values are safe to update without acquiring mutex locks, but they also pack with them mutex locks should multiple operations need to be performed together. The mutex locks, for this reason, are reentrant.
#### Atomic Integer
```php
addFunctionTask(function ($atomicInteger, $max) {
for ($i = 0; $i < $max; ++$i) {
$atomicInteger->inc();
}
}, $atomicInteger, $max);
$thread->start();
// safe
for ($i = 0; $i < $max; ++$i) {
$atomicInteger->inc();
}
// safe
while ($atomicInteger->get() !== $max * 2);
// requires mutex locking since we need to perform multiple operations together
$atomicInteger->lock();
$atomicInteger->set($atomicInteger->get() * 2);
$atomicInteger->unlock();
$thread->join();
var_dump($atomicInteger->get()); // int(400000)
```