Example of multi task scheduling with PHP co process mechanism

Keywords: Programming PHP

In the last article about Iterators, generators, and collaborators in PHP In our article, we know that in PHP, we can use the yield keyword to turn the iterator into a generator, and then through its release / recovery mechanism, we can use the cooperation mechanism to schedule in the user state. Next, we use the PHP cooperation mechanism to do an example of multi task scheduling.

1. Task execution

Task is the abstraction of a task, and a process is a user space thread, which can be understood as running a function.
So the constructor of Task is to receive a closure function, which we call coroutine.

/**
 * Task Task class
 */
class Task
{
    protected $taskId;
    protected $coroutine;
    protected $beforeFirstYield = true; //Before the first yield
    protected $sendValue;

    /**
     * Task constructor.
     * @param $taskId
     * @param Generator $coroutine
     */
    public function __construct($taskId, Generator $coroutine)
    {
        $this->taskId = $taskId;
        $this->coroutine = $coroutine;
    }

    /**
     * Get the ID of the current Task
     * 
     * @return mixed
     */
    public function getTaskId()
    {
        return $this->taskId;
    }

    /**
     * Judge whether the Task is completed
     * 
     * @return bool
     */
    public function isFinished()
    {
        return !$this->coroutine->valid();
    }

    /**
     * Set the value to be passed to the collaboration next time, such as $id = (yield $xxxx), and the value will be given to $ID
     * 
     * @param $value
     */
    public function setSendValue($value)
    {
        $this->sendValue = $value;
    }

    /**
     * Operation task
     * 
     * @return mixed
     */
    public function run()
    {
        // Note here that the start of the generator reset s, so the first value is obtained with current
        if ($this->beforeFirstYield) {
            $this->beforeFirstYield = false;
            return $this->coroutine->current();
        } else {
            // Pass parameters to generator with send
            $retval = $this->coroutine->send($this->sendValue);
            $this->sendValue = null;
            return $retval;
        }
    }
}

 

2. Task scheduling Scheduler

Next is the key part of Scheduler, which plays the role of dispatcher.

<?php

/**
 * Class Scheduler
 */
Class Scheduler
{
    /**
     * @var SplQueue
     */
    protected $taskQueue;
    /**
     * @var int
     */
    protected $tid = 0;

    /**
     * Scheduler constructor.
     */
    public function __construct()
    {
        /* The principle is to maintain a queue,
         * As mentioned before, from a programming point of view, the idea of cooperation is essentially the mechanism of active yield and resume of control flow
         */
        $this->taskQueue = new SplQueue();
    }

    /**
     * Add a task
     *
     * @param Generator $task
     * @return int
     */
    public function addTask(Generator $task)
    {
        $tid = $this->tid;
        $task = new Task($tid, $task);
        $this->schedule($task);
        $this->tid++;
        return $tid;
    }

    /**
     * Put the task in the queue
     *
     * @param Task $task
     */
    public function schedule(Task $task)
    {
        $this->taskQueue->enqueue($task);
    }

    /**
     * Run scheduler
     */
    public function run()
    {
        while (!$this->taskQueue->isEmpty()) {
            // Task out team
            $task = $this->taskQueue->dequeue();
            $res = $task->run(); // Run the task until yield

            if (!$task->isFinished()) {
                $this->schedule($task); // If the task hasn't been completed, join the team and wait for the next time
            }
        }
    }
}

In this way, we basically implement a scheduler.

You can use the following code to test:

<?php

function task1() {
    for ($i = 1; $i <= 10; ++$i) {
        echo "This is task 1 iteration $i.\n";
        yield; // Actively release the execution right of CPU
    }
}
 
function task2() {
    for ($i = 1; $i <= 5; ++$i) {
        echo "This is task 2 iteration $i.\n";
        yield; // Actively release the execution right of CPU
    }
}
 
$scheduler = new Scheduler; // Instantiate a scheduler
$scheduler->addTask(task1()); // Add different closure functions as tasks
$scheduler->addTask(task2());
$scheduler->run();

Let's go a step further and see where we can use the process.

function task1() {
    /* This is a remote task, which takes 10 seconds. It may be a task for a remote machine to grab and analyze the remote web address. We just need to submit the final result to the remote machine */
    remote_task_commit();
    // After the above request is sent out, we don't want to wait here, save the data field through yield, and actively give the execution right of CPU to task2 to run
    yield;
    //Recover the data field, take the result from the remote machine, and return the result to the caller through yield
    yield (remote_task_receive());
    ...
}
 
function task2() {
    for ($i = 1; $i <= 5; ++$i) {
        echo "This is task 2 iteration $i.\n";
        yield; // Actively release the execution right of CPU
    }
}

This improves the efficiency of the program.

Note that if yield is used in a function, the function cannot be called as a normal function. If you nest another function in a function, the call will be unsuccessful. It is easy to understand that yield needs to save and recover data on site. If it is nested, the saved and recovered data will be nested, resulting in errors. In order to avoid this situation, we need to use the method of cooperation stack, and PHP7.0 introduces yield from to implement the cooperation stack.

3. yield from keyword in PHP7

echoTimes function

function echoTimes($msg, $max) {
    for ($i = 1; $i <= $max; ++$i) {
        echo "$msg iteration $i\n";
        yield;
    }
}

task1 generator

function task1()
{
    yield from echoTimes('bar', 5);
}

In this way, it's easy to call a child process.

4, summarize

So far, we have implemented a set of task scheduling system through PHP keyword yield, generator, send(), current(), valid(). And know that yield from can implement stack / sub cooperation. Of course, in practice, we may not need to implement this system ourselves, because swoole The framework already supports the coordination mechanism. Next, we will learn about the framework.

 

Reference: https://segmentfault.com/a/1190000012457145?utm_source=tuicool&utm_medium=referral

Posted by FURQAN on Sun, 19 Apr 2020 09:17:33 -0700