Examples of using worker and pool in PHP pthreads V3

Time:2021-6-16

This article describes the use of worker and pool in PHP pthreads v3. The details are as follows:

Some people will think, clearly using thread can already work well, why do you want to set up a worker and pool?

The reason why we need to use worker and pool is because of efficiency, because it is more expensive for the system to create a new thread, and each created thread will copy the entire context of the current execution.

Reusing threads as much as possible can make our program more efficient.

A simple worker example:

<?php
//Create a custom work class and give work a name for easy viewing
class Work extends Worker
{
  private $name;

  public function __construct($name)
  {
    $this->name = $name;
  }

  public function getName()
  {
    return $this->name;
  }
}

class Task extends Thread
{
  private $num;

  public function __construct($num)
  {
    $this->num = $num;
  }

  public function run()
  {
    //Calculate the cumulative sum
    $total = 0;
    for ($i = 0; $i < $this->num; $i++) {
      $total += $i;
    }
    echo "work : {$this->worker->getName()} task : {$total} \n";
    sleep(1);
  }
}

//Create a worker thread
$work = new Work('a');

$work->start();

for ($i = 1; $i <= 10; $i++) {
  //Push task object into worker thread
  //At this time, the task object can use the worker thread context (variables, functions, etc.)
  $work->stack(new Task($i));
}

//The cleaning task of the loop will block the main thread until all the tasks in the stack are finished
while ($work->collect()) ;

//Close worker
$work->shutdown();

When the above code is running, the calculation result will come out every second, that is, 10 task objects are running on a worker thread.

If the 10 task objects are run separately in a separate space, the sleep() function will not work, and their respective sleep will not affect other threads.

Modify the above code:

<?php
//Create a custom work class and give work a name for easy viewing
class Work extends Worker
{
  private $name;

  public function __construct($name)
  {
    $this->name = $name;
  }

  public function getName()
  {
    return $this->name;
  }
}

class Task extends Thread
{
  private $num;

  public function __construct($num)
  {
    $this->num = $num;
  }

  public function run()
  {
    //Calculate the cumulative sum
    $total = 0;
    for ($i = 0; $i < $this->num; $i++) {
      $total += $i;
    }
    echo "work : {$this->worker->getName()} task : {$total} \n";
    sleep(1);
  }
}

//Create two worker threads
$work1 = new Work('a');
$work2 = new Work('b');

$work1->start();
$work2->start();

for ($i = 1; $i <= 10; $i++) {
  if ($i <= 5) {
    $work1->stack(new Task($i));
  } else {
    $work2->stack(new Task($i));
  }
}

//The cleaning task of the loop will block the main thread until all the tasks in the stack are finished
while ($work1->collect() || $work2->collect()) ;

//Close worker
$work1->shutdown();
$work2->shutdown();

Here, we create two worker threads to stack 10 task objects into two workers.

At this time, you can see that the calculation results are one-to-one, indicating that 10 task objects are running on two worker threads.

As for how many worker threads and task objects need to be created, it depends on your own requirements.

Another advantage of worker is that it can reuse objects and methods in worker. We can create a connection database object in the worker to facilitate the call of each task.

<?php
class DB extends Worker
{
  //Note that the PDO connection itself cannot be shared in the context if it is set as a static member
  //Declare as a static member, so that each worker has its own PDO connection
  private static $db = null;
  public $msg = 'i from db';

  public function run()
  {
    self::$db = new PDO('mysql:host=192.168.33.226;port=3306;dbname=test;charset=utf8', 'root', '');
  }

  public function getDb()
  {
    return self::$db;
  }
}

class Task extends Thread
{
  private $id;
  //Note that do not set the default value for the member. The $result member is a thread object and cannot be rewritten
  private $result;

  public function __construct($id)
  {
    $this->id = $id;
  }

  public function run()
  {
    //Get the database connection in worker
    $db = $this->worker->getDb();
    $ret = $db->query("select * from tb_user where id = {$this->id}");
    $this->result = $ret->fetch(PDO::FETCH_ASSOC);
    //Accessing the member variable MSG in worker
    echo "data : {$this->result['id']} {$this->result['name']} \t worker data : {$this->worker->msg} \n";
  }
}

//Create a worker thread
$work = new DB();

$work->start();

for ($i = 1; $i <= 5; $i++) {
  $work->stack(new Task($i));
}

//The cleaning task of the loop will block the main thread until all the tasks in the stack are finished
while ($work->collect()) ;

//Close worker
$work->shutdown();

tb_ You can create the user table at will. I only create the ID and name fields here for demonstration

The results are as follows

 

If workers are reusing threads, pool is a higher abstraction for workers, which can manage multiple workers at the same time.

<?php
//The reason why we want to create an ID thread class is to get a different ID for the work, which task threads belong to which work
class Id extends Thread
{
  private $id;

  public function getId()
  {
    //To prevent ID confusion, use synchronous operation here
    $this->synchronized(function () {
      ++$this->id;
    });
    return $this->id;
  }
}

class Work extends Worker
{
  private $id;

  public function __construct(Id $obj)
  {
    $this->id = $obj->getId();
  }

  public function getId()
  {
    return $this->id;
  }
}

class Task extends Thread
{
  private $num = 0;

  public function __construct($num)
  {
    $this->num = $num;
  }

  //Calculate the cumulative sum
  public function run()
  {
    $total = 0;
    for ($i = 0; $i < $this->num; $i++) {
      $total += $i;
    }
    echo "work id : {$this->worker->getId()} task : {$total} \n";
  }
}

//Create a pool to hold three work objects
$pool = new Pool(3, 'Work', [new Id()]);

//Circularly submit 20 task threads to the work object in the pool to run
for ($i = 1; $i <= 20; $i++) {
  $pool->submit(new Task($i));
}

//The cleaning task of the loop will block the main thread until the task is finished
while ($pool->collect()) ;

//Close pool
$pool->shutdown();

The results are as follows

For more information about PHP, readers interested in this site can see the following topics: summary of PHP process and thread operation skills, summary of PHP network programming skills, introductory course of PHP basic syntax, complete collection of PHP array operation skills, summary of PHP string usage “PHP + MySQL database operation tutorial” and “PHP common database operation skills summary”

I hope this article is helpful for PHP programming.