Voting

: min(two, nine)?
(Example: nine)

The Note You're Voting On

Ali Madadi
10 months 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 a new tick function for scheduling this fiber
                 
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;
    }

   
// running Fiber::suspend() in this if condition will prevent an infinite loop!
   
if(count(self::$fibers) > 1)
    {
     
Fiber::suspend();
    }
  }
}

?>

And here is an example code on how to use above Thread class:

<?php

// defining a non-blocking thread, so multiple calls will run in concurrent mode using above Thread class.
function thread (string $print, int $loop)
{
 
$i = $loop;
  while (
$i--){
    echo
$print;
  }

  return
"Thread '{$print}' finished after printing '{$print}' for {$loop} times!";
}

// registering 6 Threads (A, B, C, D, E, and F)
foreach(range('A', 'F') as $c) {
 
Thread::register($c, 'thread', [$c, rand(5, 20)]);
}

// run threads and wait until execution finishes
$outputs = Thread::run();

// print outputs
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!
)

<< Back to user notes page

To Top