Detailed introduction to coroutines in php (code)
This article first introduces the concept of generator, focusing on the usage of yield and the interface of generator. The coroutine part briefly explains the principles of coroutines and matters that should be paid attention to in PHP coroutine programming.
PHP has introduced generator (Generator) since 5.5, based on which coroutine programming can be realized. This article starts with a review of generators and then transitions to coroutine programming.
yield and generator
Generator
Generator is a data type that implements the iterator interface. The generator instance cannot be obtained through new, and there is no static method to obtain the generator instance. The only way to get a generator instance is to call the generator function (a function containing the yield keyword). Calling the generator function directly returns a generator object, and the code within the function starts executing when the generator is running.
First go to the code to intuitively experience yield and generators:
# generator1.php function foo() { exit('exit script when generator runs.'); yield; } $gen = foo(); var_dump($gen); $gen->current(); echo 'unreachable code!'; # 执行结果 object(Generator)#1 (0) { } exit script when generator runs.
foo
The function contains the yield
keyword and transforms into a generator function. Calling foo
does not execute any code in the function body, but instead returns a generator instance. After the generator runs, the code within the foo
function is executed and the script ends.
As the name suggests, generators can be used to generate data. It’s just that the way it generates data is different from other functions: the generator returns data through yield
instead of return
; After yield
returns data, the generator function does not It will be destroyed, it just pauses the operation, and it can be resumed from the pause in the future; the generator will return (only) one data if it is run once, and multiple data will be returned if it is run multiple times; if the generator is not called to obtain data, the code in the generator will lie there. The so-called "moving every time" refers to the way the generator generates data.
The generator implements the iterator interface. To obtain the generator data, you can use foreach
loop or manual current/next/valid
. The following code demonstrates data generation and traversal:
# generator2.php function foo() { # 返回键值对数据 yield "key1" => "value1"; $count = 0; while ($count < 5) { # 返回值,key自动生成 yield $count; ++ $count; } # 不返回值,相当于返回null yield; } # 手动获取生成器数据 $gen = foo(); while ($gen->valid()) { fwrite(STDOUT, "key:{$gen->key()}, value:{$gen->current()}\n"); $gen->next(); } # foreach 遍历数据 fwrite(STDOUT, "\ndata from foreach\n"); foreach (foo() as $key => $value) { fwrite(STDOUT, "key:$key, value:$value\n"); }
yield
yield
The keyword is the core of the generator, which allows ordinary functions to differentiate (evolve) into generator functions. yield
means "giving up". When the program executes to the yield
statement, it will pause execution, yield the CPU and return control to the caller. The next execution will continue from the interruption point. implement. When control returns to the caller, the yield
statement can return the value to the caller. generator2.php
The script demonstrates the three forms of yield return values:
yield $key => $value: Returns the key and value of the data;
yield $value: Returns data, key is allocated by the system;
yield: Returns null value, key is allocated by the system;
yield
Allows the function to pause, continue execution at any time, and return data to the caller. If external data is needed to continue execution, this work is provided by the send
function of the generator: the variable that appears on the left side of yield
will receive the variable from send
value. Let’s look at a common send
function usage example:
function logger(string $filename) { $fd = fopen($filename, 'w+'); while($msg = yield) { fwrite($fd, date('Y-m-d H:i:s') . ':' . $msg . PHP_EOL); } fclose($fd); } $logger = logger('log.txt'); $logger->send('program starts!'); // do some thing $logger->send('program ends!');
send
The ability to have two-way data communication between generators and the outside: yield
Return data; send
Provide support data for continued operation. Since send
allows the generator to continue executing, this behavior is similar to the next
interface of the iterator, where next
is equivalent to send(null)
.
Others
$string = yield $data;
expression is not legal before PHP7 and requires parentheses:$string = (yield $data)
;- ##PHP5 generator function cannot
return
value, after PHP7 it can return value and pass the
of the generator getReturnGet the returned value.
- PHP7 adds the
yield from
syntax to implement generator delegation.
- The generator is a one-way iterator, and
rewind
cannot be called after it is started.
- Data generation (producer), returning data through yield;
- Data consumption (consumer) ), consume the data from send;
- implements the coroutine.
2gua: PHP Generator is lively, interesting and easy to understand.
Coroutine programmingCoroutine (coroutine) is a subroutine that can be interrupted and resumed at any time. Theyield keyword allows the function to have this ability, so it can be used For coroutine programming.
Generator and coroutine
The coroutine implemented by the generator is a stackless coroutine, that is, the generator function only has a function frame, which is attached to the caller's stack during runtime for execution. Unlike the powerful stackful coroutine, the generator cannot control the direction of the program after it is paused, and can only passively return control to the caller; the generator can only interrupt itself, not the entire coroutine. Of course, the advantage of the generator is that it is highly efficient (you only need to save the program counter when pausing) and is simple to implement.
Coroutine Programming
Speaking of coroutine programming in PHP, I believe most people have read this blog post reprinted (translated) by Brother Niao: Using coroutines to achieve multiple functions in PHP Task scheduling. The original author nikic is the core developer of PHP, the initiator and implementer of the generator function. If you want to learn more about generators and coroutine programming based on them, nikic's RFC on generators and the articles on Niaoge's website are must-reads.
Let’s first look at the working method of coroutines based on generators: coroutines work collaboratively, that is, coroutines actively give up the CPU to achieve alternate running of multiple tasks (that is, concurrent multitasking, but not parallel); A generator can be regarded as a coroutine. When the yield
statement is executed, the CPU control is returned to the caller, and the caller continues to execute other coroutines or other codes.
Let’s look at the difficulty in understanding Brother Bird’s blog. Coroutines are very lightweight, and thousands of coroutines (generators) can exist in a system at the same time. The operating system does not schedule coroutines, and the work of arranging coroutine execution falls on developers. Some people don’t understand the coroutine part of Brother Niao’s article because it says there is little coroutine programming (writing coroutines mainly means writing generator functions), but they spend a lot of time implementing a coroutine scheduler (scheduler or kernel). : Simulates the operating system and performs fair scheduling on all coroutines. The general thinking of PHP development is: I wrote these codes, and the PHP engine will call my codes to get the expected results. Coroutine programming requires not only writing code to do the work, but also writing code to instruct these codes when to work. If you don’t have a good grasp of the author’s thinking, it will naturally be more difficult to understand. It needs to be scheduled by itself, which is a disadvantage of the generator coroutine compared to the native coroutine (async/await form).
Now that we know what coroutine is, what can it be used for? The coroutine gives up the CPU on its own to cooperate and efficiently utilize the CPU. Of course, the time to give up should be when the program is blocked. Where will the program block? User-mode code rarely blocks, and blocking is mainly caused by system calls. The majority of system calls are IO, so the main application scenario of coroutines is network programming. In order to make the program high performance and high concurrency, the program should execute asynchronously and not block. Since asynchronous execution requires notifications and callbacks, writing callback functions cannot avoid the problem of "callback hell": code readability is poor, and the program execution process is scattered among layers of callback functions. There are two main ways to solve callback hell: Promise and coroutines. Coroutines can write code in a synchronous manner and are recommended in high-performance network programming (IO-intensive).
Let’s look back at coroutine programming in PHP. In PHP, coroutine programming is implemented based on generators. It is recommended to use coroutine frameworks such as RecoilPHP
and Amp
. These frameworks have already written schedulers. If you develop a generator function directly on it, the kernel will automatically schedule execution (if you want a function to be scheduled for execution in a coroutine mode, just add yield
to the function body. ). If you don’t want to use the yield
method for coroutine programming, we recommend swoole
or its derivative framework, which can achieve a coroutine programming experience similar to golang and enjoy the development efficiency of PHP.
If you want to use the original PHP coroutine programming, a scheduler similar to the one in Niao Ge's blog is essential. The scheduler schedules the execution of the coroutine. After the coroutine is interrupted, control returns to the scheduler. Therefore, the scheduler should always be in the main (event) loop, that is, when the CPU is not executing the coroutine, it should be executing the scheduler code. When running without a coroutine, the scheduler should block itself to avoid consuming the CPU (Niao Ge's blog uses the built-in select
system call), wait for the event to arrive, and then execute the corresponding coroutine. During the running of the program, except for scheduler blocking, the coroutine should not call blocking APIs during running.
Summary
In coroutine programming, the main function of yield
is to transfer control without worrying about its return value (basically yield
The returned value will be send
directly during the next execution). The focus should be on the timing of the transfer of control and how the coroutine operates.
In addition, it needs to be explained that coroutines have little to do with asynchrony, and it also depends on the running environment support. In the conventional PHP operating environment, even if promise/coroutine is used, it is still blocked synchronously. No matter how awesome the coroutine framework is, sleep
is no longer easy to use. As an analogy, even if JavaScript does not use promise/async technologies, it is asynchronous and non-blocking.
Through generators and Promise, coroutine programming similar to await
can be implemented. There is a lot of relevant code on Github and will not be given in this article.
Related recommendations:
$_SERVER in PHP Detailed introduction
Detailed introduction to output_buffering in PHP, outputbuffering_PHP tutorial
The above is the detailed content of Detailed introduction to coroutines in php (code). For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

If you are an experienced PHP developer, you might have the feeling that you’ve been there and done that already.You have developed a significant number of applications, debugged millions of lines of code, and tweaked a bunch of scripts to achieve op

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.
