Analysis of asynchronous method based on co process in php

  • 2021-12-13 07:39:03
  • OfStack

In this paper, an example is given to describe the asynchronous method of php based on co-process. Share it for your reference, as follows:

php on github is mostly implemented according to this article: http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html.

They all end up turning callbacks into elegantly sequential code, but still blocking, not really asynchronous.

For example, the hottest one: https://github.com/recoilphp/recoil

Install first:


composer require recoil/recoil

Execution:


<?php
//recoil.php
include __DIR__ . '/vendor/autoload.php';
use Recoil\React\ReactKernel;
$i = 100000;
ReactKernel::start(task1());
ReactKernel::start(task2());
function task1(){
  global $i;
  echo "wait start" . PHP_EOL;
  while ($i-- > 0) {
    yield;
  }
  echo "wait end" . PHP_EOL;
};
function task2(){
  echo "Hello " . PHP_EOL;
  yield;
  echo "world!" . PHP_EOL;
}

Results:

wait start
//Wait a few seconds
wait end
Hello
world!

I wanted to have two tasks in parallel, but the two tasks became serial, and nothing could be done during the waiting time. React responsive programming is strictly prohibited this kind of waiting, so I refer to the unity3d co-process to write their own version of php. On the code:


<?php
//Coroutine.php
// Dependency swoole The timer can also be realized by other methods 
class Coroutine
{
  // You can change the timer interval, unit as needed ms
  const TICK_INTERVAL = 1;
  private $routineList;
  private $tickId = -1;
  public function __construct()
  {
    $this->routineList = [];
  }
  public function start(Generator $routine)
  {
    $task = new Task($routine);
    $this->routineList[] = $task;
    $this->startTick();
  }
  public function stop(Generator $routine)
  {
    foreach ($this->routineList as $k => $task) {
      if($task->getRoutine() == $routine){
        unset($this->routineList[$k]);
      }
    }
  }
  private function startTick()
  {
    swoole_timer_tick(self::TICK_INTERVAL, function($timerId){
      $this->tickId = $timerId;
      $this->run();
    });
  }
  private function stopTick()
  {
    if($this->tickId >= 0) {
      swoole_timer_clear($this->tickId);
    }
  }
  private function run()
  {
    if(empty($this->routineList)){
      $this->stopTick();
      return;
    }
    foreach ($this->routineList as $k => $task) {
      $task->run();
      if($task->isFinished()){
        unset($this->routineList[$k]);
      }
    }
  }
  
}
class Task
{
  protected $stack;
  protected $routine;
  public function __construct(Generator $routine)
  {
    $this->routine = $routine;
    $this->stack = new SplStack();
  }
  /**
   * [run  Co-scheduling ]
   * @return [type]     [description]
   */
  public function run()
  {
    $routine = &$this->routine;
    try {
      if(!$routine){
        return;
      }
      $value = $routine->current();
      // Nested co-process 
      if ($value instanceof Generator) {
        $this->stack->push($routine);
        $routine = $value;
        return;
      }
      // Nested co-routine return 
      if(!$routine->valid() && !$this->stack->isEmpty()) {
        $routine = $this->stack->pop();
      }
      $routine->next();
    } catch (Exception $e) {
      if ($this->stack->isEmpty()) {
        /*
          throw the exception
        */
        return;
      }
    }
  }
  /**
   * [isFinished  Determine the task Whether or not to complete ]
   * @return boolean [description]
   */
  public function isFinished()
  {
    return $this->stack->isEmpty() && !$this->routine->valid();
  }
  public function getRoutine()
  {
    return $this->routine;
  }
}

Test code:


<?php
//test.php
 require 'Coroutine.php';
$i = 10000;
$c = new Coroutine();
$c->start(task1());
$c->start(task2());
function task1(){
  global $i;
  echo "wait start" . PHP_EOL;
  while ($i-- > 0) {
    yield;
  }
  echo "wait end" . PHP_EOL;
};
function task2(){
  echo "Hello " . PHP_EOL;
  yield;
  echo "world!" . PHP_EOL;
}

Results:

wait start
Hello
world!
//Wait for a few seconds without blocking
wait end

For more readers interested in PHP related contents, please check the topics of this site: "PHP Extended Development Tutorial", "PHP Network Programming Skills Summary", "php curl Usage Summary", "PHP Array (Array) Operation Skills Complete Book", "PHP Data Structure and Algorithm Tutorial", "php Programming Algorithm Summary" and "php String (string) Usage Summary"

I hope this article is helpful to everyone's PHP programming.


Related articles: