PHP advanced tutorial-how to play PHP coroutine? This article will take you through the swoole coroutine

Coroutines are complex and not complicated, but difficult and not difficult. One sentence can be summarized: it can improve concurrency but cannot speed up tasks, synchronous code implements asynchronous IO, and asynchronous non-blocking code blocks.

A coroutine is a special function, a function that can be suspended, and then resume execution from where it was suspended. Multiple coroutines in a thread are serial, just like the CPU processing process. A coroutine can run on a thread, unless control is given to other coroutines to run. The coroutine cannot use the multi-core CPU, so the coroutine can only solve the concurrency problem, not the task processing speed problem. A coroutine is to divide a large task into smaller fragments and encapsulate a function of the process. When one of the coroutines needs to be blocked by IO, it actively suspends the current coroutine and transfers control to other coroutines to run.

We know that processes and threads are scheduled by the operating system. When to execute depends on when the operating system gives CPU time to a process or thread, and when the coroutine surrenders control is determined by the user. Processes and threads belong to kernel mode, and coroutines belong to user mode threads.

The coroutine is a lightweight thread in user mode, and the scheduling of the coroutine is completely controlled by the user. The coroutine has its own register context and stack. When the coroutine is scheduled to switch, save the register context and stack to other places. When switching back, restore the previously saved register context and stack. Directly operating the stack will basically have no core switching overhead, and you can access global variables without locking. , So the context switching is very fast.

My penguins exchange oh together

Coroutine features

  • User mode threads, when encountering IO, actively give up control

  • Multiple coroutine codes are still serial, no need to lock

  • Low overhead, only occupies memory, no process and thread switching overhead

  • Large amount of concurrency, a single process can open 50w coroutines

  • Anytime, anywhere, as long as you want to be concurrent, call go to create a coroutine

Insert picture description here

We know that threads are lightweight processes, so coroutines are lightweight threads. Coroutines run on threads, and a thread can have multiple coroutines.

We know that when the process encounters blockage, one more thread is opened to switch within the process to avoid switching the process every time, so that the usable time allocated by the CPU to this process can be used more vigorously. The relationship between a coroutine and a thread and a process is very similar, except that a coroutine has a direct relationship with a thread.

Insert picture description here

The above figure is a schematic diagram of switching between multiple threads, so let's consider, if a thread is just waiting for IO operations (network or file), then why do you reuse this thread like a thread reuses a process? Let's remove the IO and see what this graph looks like.

Insert picture description here

Remove the IO part of the operation, it can be seen that basically this concurrent request application code can run in a single thread, and the coroutine makes the most of the time the thread waits for IO, so that the program can execute other business codes while waiting for IO.

Insert picture description here

Does it look like a thread's execution flow? This is the charm of a coroutine. When a coroutine is yielded, it will be suspended and transfer control to other coroutines within the thread, because it is performed on the thread. Switching, so the overhead is much lower than processes and threads.

Insert picture description here

When the program calls the coroutine, the current coroutine will actively give up control to other coroutines in the same thread for processing. As shown in the figure, when the developer's code needs to use IO, it will actively give up control of the coroutine Used by other coroutines.

Insert picture description here

Remove the IO part and look at the processing of the coroutine. All business logic is directly executed, avoiding the IO causing the thread to switch to the waiting state, and making full use of the execution time allocated by the CPU to this thread.

Note: The coroutine does not speed up tasks, it can only perform more tasks.

Because coroutines are built on threads, there is no way to use the advantages of CPU multi-core. Coroutines are suitable for IO-intensive computing scenarios.

What is the role of coroutines?

The coroutine is to increase the CPU usage and avoid a large number of thread context switching when the thread is blocked.

echo "1-start\n";
sleep(1);
echo "1-end\n";
echo "2-start\n";
sleep(1);
echo "2-end\n";
echo "3-start\n";
sleep(1);
echo "3-end\n";
echo "4-start\n";
sleep(1);
echo "4-end\n";

Insert picture description here

The CPU usage of the above code is only 1%

Swoole\Runtime::enableCoroutine(true);
go(function () {
    
    
    echo "go1-start\n";
    sleep(1);
    echo "go1-end\n";
});
go(function () {
    
    
    echo "go2-start\n";
    sleep(1);
    echo "go2-end\n";
});
go(function () {
    
    
    echo "go3-start\n";
    sleep(1);
    echo "go3-end\n";
});
go(function () {
    
    
    echo "go4-start\n";
    sleep(1);
    echo "go4-end\n";
});

Insert picture description here

Using the coroutine, the CPU usage was successfully increased to 4%, so that the CPU does not need to run idly for IO blocking or perform context switching. Didn’t it say that coroutines cannot be accelerated? After using the coroutine here, why is it executed in more than 1 second, which is different from the previous code? The time obtained here depends on the time when the last coroutine execution ends.

The execution order of the coroutine

Swoole\Runtime::enableCoroutine(true);
go(function(){
    
    
   sleep(2);
   echo "go1\n";
});
go(function(){
    
    
    sleep(1);
    echo "go2\n";
});
echo "main\n";

First output: main->go2->go1

Communication between coroutines

Communication between multiple coroutines is realized by Channel, and multiple coroutines assist in completing common tasks.

Swoole\Runtime::enableCoroutine(true);
$chan = new Swoole\Coroutine\Channel();
go(function () use ($chan){
    
    
    sleep(1);
    $chan->push(['name'=>'sunny']);
});

go(function() use ($chan){
    
    
    $data = $chan->pop();
    print_r($data);
});
echo "结束\n";

Actual combat: realize waitGroup function

Use the Channel provided by Swoole to implement a waitGroup, the main function is to wait for the completion of all coroutines.

<?php
class WaitGroup{
    
    
    private $count;
    private $chan;
    public function __construct()
{
    
    
        $this->chan = new Swoole\Coroutine\Channel();
    }

    public function add(){
    
    
        $this->count++;
    }
    public function done(){
    
    
        $this->chan->push(true);
    }

    public function wait(){
    
    
        for($i=0;$i<$this->count;$i++){
    
    
            $this->chan->pop();
        }
    }

}

<?php
include 'waitgroup.php';
Swoole\Runtime::enableCoroutine(true);
echo "start".PHP_EOL;
$t = microtime(true);
go(function() use ($t){
    
    
    $wg = new WaitGroup();
    $wg->add();
    go(function() use ($t,&$wg){
    
    
        echo file_get_contents("https://www.sunnyos.com/swoole.php");
        echo "协程1:".(microtime(true)-$t).PHP_EOL;
        $wg->done();
    });
    $wg->add();
    go(function() use ($t,&$wg){
    
    
        echo file_get_contents("https://www.sunnyos.com/swoole.php");
        echo "协程2:".(microtime(true)-$t).PHP_EOL;
        $wg->done();
    });
    $wg->add();
    go(function() use ($t,&$wg){
    
    
        echo file_get_contents("https://www.sunnyos.com/swoole.php");
        echo "协程3:".(microtime(true)-$t).PHP_EOL;
        $wg->done();
    });
    $wg->wait();
    echo '全部结束:'.(microtime(true)-$t).PHP_EOL;
});
echo "end".PHP_EOL;
echo microtime(true)-$t.PHP_EOL;

In the code: https://www.sunnyos.com/swoole.php swoole.php code

<?php
sleep(1);
echo "My name is Sunny\n";

Dormant vaccine simulation network request time-consuming

Insert picture description here

Here is a look at the three goroutines used to make network requests. Each request takes 1 second, but when the three requests are executed here, it only takes 1.2 seconds to execute, but the CPU usage rate is 6 %, this shows that the coroutine fully uses the cpu.

Pay attention, don't get lost

Alright, everyone, the above is the entire content of this article. The people who can see here are all talents . As I said before, there are a lot of technical points in PHP, because there are too many, it is really impossible to write, and you will not read too much after writing it, so I will organize it into PDF and documents here, if necessary Can

Click to enter the secret code: PHP+「Platform」

Insert picture description here

Insert picture description here


For more learning content, please visit the [Comparative Standard Factory] excellent PHP architect tutorial catalog, as long as you can read it to ensure that the salary will rise a step (continuous update)

The above content hopes to help everyone . Many PHPers always encounter some problems and bottlenecks when they are advanced. There is no sense of direction when writing too much business code. I don’t know where to start to improve. I have compiled some information about this, including But not limited to: distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, Mysql optimization, shell scripts, Docker, microservices, Nginx, etc. Many knowledge points, advanced advanced dry goods, can be shared with everyone for free, and those who need can join my PHP technology exchange group

Guess you like

Origin blog.csdn.net/weixin_49163826/article/details/109189754