php coroutine

  • In relation to the iterative generator in this blog

Coroutine

  • Coroutine support is based on an iterative generator, can increase the data sent back to the generator function (the caller sends data to the called function generator). This is the generator to change the caller-way communication two-way communication between the two.

  • Function is to transfer data through the iterator send () method implementation below Logger () coroutine example of how such communication operation:

<?php
function logger($fileName) {
    $fileHandle = fopen($fileName, 'a');
    while (true) {
        fwrite($fileHandle, yield . "\n");
    }
}
 
$logger = logger(__DIR__ . '/log');
$logger->send('Foo');
$logger->send('Bar')
?>
  • As you can see, here there is no yield is used as a statement, but as an expression, that it can be evolved into a value. This value is the value of the caller passed to the send () method . In this example, , yield expression will first be "Foo" alternative written Log, then "Bar" alternative written Log.

  • In the above example demonstrates the yield as recipients, let's look at an example of how to receive and transmit at the same time:

<?php
function gen() {
    $ret = (yield 'yield1');  //这儿yield没有作为一个语句来使用, 而是用作一个表达式, 即它能被演化成一个值. 这个值就是调用者传递给send()方法的值
    var_dump($ret);
    $ret = (yield 'yield2');
    var_dump($ret);
}
 
$gen = gen();
var_dump($gen->current());        // string(6) "yield1"
var_dump($gen->send('ret1'));     // string(4) "ret1"   (the first var_dump in gen)
                                                      // string(6) "yield2" (the var_dump of the ->send() return value)
var_dump($gen->send('ret2'));     // string(4) "ret2"   (again from within gen)
                                                      // NULL               (the return value of ->send())
?>
  • To understand the exact sequence of output soon may be slightly difficult, but you sure you want to figure out why the output in this way. Continue reading to follow.

  • In addition, I must point out two things:

  • The first point, yield both sides of the expression in parentheses before PHP7 not optional, that is to say in PHP5.5 and PHP5.6 in parentheses are necessary.

  • Second, you may have noticed call current () before calling no rewind (). This is because the time has generated an iteration object implicitly perform the rewind operation.

Multitasking Collaboration

  • If you read the above logger () example, you might be wondering "for two-way communication Why should I use the coroutine? I can use other non-coroutines way to achieve the same function ah?" Yes, you're right. but only to the above examples demonstrate the basic usage, in fact, this example does not really demonstrate the advantages of using coroutines.

  • As mentioned in the introduction above, coroutine is a very powerful concept, but it is very rare and often use very complex. To give some examples of simple but real hard.

  • In this article, I decided to do is to use a multi-task coroutine cooperation. We want to solve the problem is you want to concurrently run multiple tasks (or "Program"). But we all know that CPU at a time only run a task (without regard to multicore case). So the processor needs to switch between different tasks, so that each task and always run "for a little while."

  • Multitasking term cooperation in the "Collaboration" very good description of how such a switch: it requires task is currently running automatically control back to the scheduler so you can run other tasks with this "preemption. "Instead multitasking, preemptive multitasking is this: the scheduler can interrupt the operation of the task for some time, whether it likes it or not cooperative multitasking have used in earlier versions of Windows (windows95) and Mac OS, but they are. later switch to the use of preemptive multitasking reason is quite clear: if you rely on the program automatically relinquish control, then some malicious programs will easily occupy the entire CPU, not shared with other tasks.

  • Now you should understand the relationship between a coroutine and task scheduling: yield instruction provides task interruption itself a way, and then return control to the task scheduler therefore coroutine can run multiple other tasks Still further. , yield can also be used to communicate between the tasks and the scheduler.

  • In order to achieve our multi-task scheduling, first to achieve "mission" - coroutine function with a lightweight package:
<?php
class Task {
    protected $taskId;
    protected $coroutine;
    protected $sendValue = null;
    protected $beforeFirstYield = true;
 
    public function __construct($taskId, Generator $coroutine) {
        $this->taskId = $taskId;
        $this->coroutine = $coroutine;
    }
 
    public function getTaskId() {
        return $this->taskId;
    }
 
    public function setSendValue($sendValue) {
        $this->sendValue = $sendValue;
    }
 
    public function run() {
        if ($this->beforeFirstYield) {
            $this->beforeFirstYield = false;
            return $this->coroutine->current();
        } else {
            $retval = $this->coroutine->send($this->sendValue);
            $this->sendValue = null;
            return $retval;
        }
    }
 
    public function isFinished() {
        return !$this->coroutine->valid();
    }
}

Such as code, a task with the task ID is the mark of a coroutine (function) Use setSendValue () method, you can specify which values ​​will be sent to the next recovery (after you will understand that we need this), run () function does not do anything, except collaborative program calls send () method, to understand why add a beforeFirstYieldflag variable, consider the following code fragment:

<?php
function gen() {
    yield 'foo';
    yield 'bar';
}
 
$gen = gen();
var_dump($gen->send('something'));
 
// 如之前提到的在send之前, 当$gen迭代器被创建的时候一个renwind()方法已经被隐式调用
// 所以实际上发生的应该类似:
//$gen->rewind();
//var_dump($gen->send('something'));
 
//这样renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
//真正当我们调用yield的时候, 我们得到的是第二个yield的值! 导致第一个yield的值被忽略.
//string(3) "bar"
  • It is returned correctly by adding beforeFirstYieldcondition we can determine the value of the first yield.

  • The scheduler now have multi-tasking cycle than do a little more, and then just run multiple tasks:

Guess you like

Origin www.cnblogs.com/lz0925/p/11431389.html