PHP协程:Go+Chan+Defer
Swoole4 provides a powerful CSP co programming mode for the PHP language. The bottom layer provides three keywords, which can easily implement various functions.
The PHP coroutine syntax provided by Swoole4 is borrowed from Golang, and we pay tribute to the GO development team
The PHP+Swoole process can complement Golang very well. Golang: static language, rigorous and powerful with good performance, PHP+Swoole: dynamic language, flexible, simple, and easy to use
This article is based on Swoole-4.2.9 and PHP-7.2.9 versions
keyword
Go: Create a collaboration
Chan: Create a channel
Defer: Delay the task, executed when the collaboration exits, first in, second out
The underlying implementation of these three functions is all memory operations, without any IO resource consumption. Just like PHP's Array, it is very cheap. You can use it directly if needed. This is different from socket and file operations, which require requesting port and file descriptors from the operating system, and reading and writing may result in blocked IO waiting.
Concurrency of collaborative processes
Using the go function allows a function to execute concurrently. During the programming process, if a certain piece of logic can be executed concurrently, it can be placed in the go process for execution.
Sequential execution
function test1()
{
sleep(1);
echo "b";
}
function test2()
{
sleep(2);
echo "c";
}
test1();
test2();
Execution results:
htf@LAPTOP-0K15EFQI:~$ time php b1.php
bc
real 0m3.080s
user 0m0.016s
sys 0m0.063s
htf@LAPTOP-0K15EFQI:~$
In the above code, test1 and test2 will be executed sequentially, taking 3 seconds to complete.
Concurrent execution
By using go to create a coroutine, two functions, test1 and test2, can be executed concurrently.
Swoole\Runtime::enableCoroutine();
go(function ()
{
sleep(1);
echo "b";
});
go(function ()
{
sleep(2);
echo "c";
});
The function of Swoole Runtime: enableCoroutine() is to switch PHP's functions such as stream, sleep, pdo, mysqli, redis, etc. from synchronous blocking to asynchronous IO of coroutines
Execution results:
bchtf@LAPTOP-0K15EFQI :~$time php co.php
BC
Real 0m2.076s
User 0m0.000s
Sys 0m0.078s
htf@LAPTOP-0K15EFQI :~$
You can see that the execution was completed in just 2 seconds.
The sequential execution time is equal to the total execution time of all tasks: t1+t2+t3
The concurrent execution time is equal to the maximum value of all task execution time: max (t1, t2, t3,...)
Collaborative communication
With the go keyword, concurrent programming becomes much simpler. At the same time, it also brings new problems. If there are two concurrent processes executing, and the other process needs to rely on the execution results of these two processes, what if this problem is solved?
The answer is to use channels, which can be created using new channels in the Swoole4 protocol. A channel can be understood as a queue with its own coordinated scheduling. It has two interfaces: push and pop:
Push: Write content to the channel. If it is full, it will enter a waiting state and automatically recover when there is space
Pop: Read content from the channel. If it is empty, it will enter a waiting state and automatically recover when there is data
Using channels can facilitate concurrency management.
$chan=new chan (2);
Synergy 1
go (function () use ($chan) {
$result = [];
for ($i = 0; $i < 2; $i++)
{
$result += $chan->pop();
}
var_dump($result);
});
Coroutine2
go(function () use ($chan) {
$cli = new Swoole\Coroutine\Http\Client('www.qq.com', 80);
$cli->set(['timeout' => 10]);
$cli->setHeaders([
'Host' => "www.qq.com",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$ret = $cli->get('/');
// $cli->body The response content is too large, here we use the HTTP status code as a test
$chan->push(['www.qq.com' => $cli->statusCode]);
});
Coroutine3
go(function () use ($chan) {
$cli = new Swoole\Coroutine\Http\Client('www.163.com', 80);
$cli->set(['timeout' => 10]);
$cli->setHeaders([
'Host' => "www.163.com",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$ret = $cli->get('/');
// $cli->body The response content is too large, here we use the HTTP status code as a test
$chan->push(['www.163.com' => $cli->statusCode]);
});
results of execution:
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php co2.php
array(2) {
["www.qq.com"]=>
int(302)
["www.163.com"]=>
int(200)
}
real 0m0.268s
user 0m0.016s
sys 0m0.109s
htf@LAPTOP-0K15EFQI :~/swoole src/examples/5.0$
Here, three processes were created using Go, with Process 2 and Process 3 requesting the homepage of qq.com and 163.com, respectively. Process 1 needs to obtain the result of the HTTP request. Here, Chan is used to implement concurrency management.
Coprocess 1 loops twice to pop the channel, as the queue is empty, it will enter a waiting state
After the execution of process 2 and process 3 is completed, the data will be pushed. Process 1 receives the results and continues to execute downwards
Delayed Tasks
In co programming, it may be necessary to automatically perform some tasks and perform cleaning work when the co program exits. Register similar to PHP_ Shutdown_ Function can be implemented using defer in Swoole4.
Swoole\Runtime::enableCoroutine();
go(function () {
echo "a";
defer(function () {
echo "~a";
});
echo "b";
defer(function () {
echo "~b";
});
sleep(1);
echo "c";
});
results of execution:
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php defer.php
abc~b~a
real 0m1.068s
user 0m0.016s
sys 0m0.047s
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$
epilogue
The Go+Chan+Defer provided by Swoole4 brings a new CSP concurrent programming mode to PHP. Flexibly using the various features provided by Swoole4 can solve the design and development of various complex functions in work.