The Note You're Voting On
Ali Madadi ¶1 year ago
Here is a simple scheduler and thread pool that implements multithreading using fibers and tick functions in PHP 8.1 and returns the return value of each function in the pool in an array at the end.
Note that due to some bugs, you need to register a new tick function for each "thread". Remember to unregister all of them at the end.
The link bellow is the discussion on a bug that is going on right now (At the time of writing this). Note that based on the discussion, the ability to call Fiber::suspend() inside tick function may become forbidden in PHP 8.2+. But if the bug gets fixed, you can move register_tick_function() line to the top of the class, and this simple multithreading class in pure PHP code will work like a charm.
https://github.com/php/php-src/issues/8960
<?php
declare(ticks=1);
class Thread {
protected static $names = [];
protected static $fibers = [];
protected static $params = [];
public static function register(string|int $name, callable $callback, array $params)
{
self::$names[] = $name;
self::$fibers[] = new Fiber($callback);
self::$params[] = $params;
}
public static function run() {
$output = [];
while (self::$fibers) {
foreach (self::$fibers as $i => $fiber) {
try {
if (!$fiber->isStarted()) {
register_tick_function('Thread::scheduler');
$fiber->start(...self::$params[$i]);
} elseif ($fiber->isTerminated()) {
$output[self::$names[$i]] = $fiber->getReturn();
unset(self::$fibers[$i]);
} elseif ($fiber->isSuspended()) {
$fiber->resume();
}
} catch (Throwable $e) {
$output[self::$names[$i]] = $e;
}
}
}
return $output;
}
public static function scheduler () {
if(Fiber::getCurrent() === null) {
return;
}
if(count(self::$fibers) > 1)
{
Fiber::suspend();
}
}
}
?>
And here is an example code on how to use above Thread class:
<?php
function thread (string $print, int $loop)
{
$i = $loop;
while ($i--){
echo $print;
}
return "Thread '{$print}' finished after printing '{$print}' for {$loop} times!";
}
foreach(range('A', 'F') as $c) {
Thread::register($c, 'thread', [$c, rand(5, 20)]);
}
$outputs = Thread::run();
echo PHP_EOL, '-------------- RETURN VALUES --------------', PHP_EOL;
print_r($outputs);
?>
The output will be something like this (but probably different):
ABCDEFABCDEFABCDEFABCDEFABCDEFABCEFABFABFABEBEFBEFEFEFAABEABEBEFBEFFAAAAAA
-------------- RETURN VALUES --------------
Array
(
[D] => Thread 'D' finished after printing 'D' for 5 times!
[C] => Thread 'C' finished after printing 'C' for 6 times!
[E] => Thread 'E' finished after printing 'E' for 15 times!
[B] => Thread 'B' finished after printing 'B' for 15 times!
[F] => Thread 'F' finished after printing 'F' for 15 times!
[A] => Thread 'A' finished after printing 'A' for 18 times!
)