入门使用

go(function(){
    for($i=0;$i<=10;$i++){
        echo "\$i:".$i.PHP_EOL;
        \Swoole\Coroutine::sleep(1);
    }
    for ($j=0;$j<=3;$j++){
        echo "\$j:".$j.PHP_EOL;
        \Swoole\Coroutine::sleep(1);
    }
});

go(function(){
   for($z=0;$z<=6;$z++){
       echo "\$z:".$z.PHP_EOL;
       \Swoole\Coroutine::sleep(1);
   }
});

echo "协程测试".PHP_EOL;

协程修改外部的变量需要加引用符号

$count=0;
go(function()use(&$count){
    for ($j=0;$j<=3;$j++){
        $count=$count+$j;
        \Swoole\Coroutine::sleep(1);
    }
});

echo "协程测试:".$count.PHP_EOL;

协程之间的通信

这里没有使用sleep函数。使用sleep函数底层会yield当前协程,让出时间片,并添加一个异步定时器,当超时时间到达时重新resume当前协程,恢复运行。

$chan=new co\Channel; 可以直接这么写。不用声明容量

//swoole4.4以后下面必须加上Swoole\Event::wait();
use  Swoole\Coroutine as co;
$count=0;
$chan=new co\Channel(1);
$cid=go(function()use($chan,$count){ //只负责计算
    for ($j=0;$j<=4;$j++){
        if($j==2){
            //push里面可以放入数字
            $arr=$j;
            //push里面也可以放数组
            $arr=['arr'=>$j];
            $chan->push($arr);
            //让位代码执行权。时间片、yield让位的意思co::yield让出执行权。。$cid是协程id。
            co::yield();
        }
        $count=$count+$j;
        echo "co1=>count:".$count.",co2=>j:{$j}".PHP_EOL;
    }

});
go(function()use($chan,$count,$cid){ //只负责输出
    for ($j=0;$j>=3;$j++){
        $count=$count+$j;
        echo "co2=>count:".$count.PHP_EOL;
    }
    $pop=$chan->pop();
    if(is_array($pop)){
        $pop=json_encode($pop);
    }
    echo "协程测试:".$pop.PHP_EOL;
    co::resume($cid); //恢复之前的携程
});
echo "携程结束".PHP_EOL;
//
//register_shutdown_function(function () {
//    Swoole\Event::wait();
//});
//或者下面4.4必须这么执行

Swoole\Event::wait();

swoole中的Runtime

底层增加一个新的特性,可以在运行时动态将基于php_stream实现的扩展、PHP网络客户端代码一键协程化。

文档传送门:https://wiki.swoole.com/wiki/page/p-runtime.html

如果不在swoole协程自带的列表中。使用协程会变成阻塞形式。

准备1.后台代码index.php开启服务器

$t="Guest";
if(isset($_GET['t'])){
    sleep(3);
    $t=$_GET['t'];
}

$html=<<<data
    this is server, user is:$t;
data;

exit($html);

访问代码

use  Swoole\Coroutine as co;
function getHtml($t=false){
    $url="http://localhost/";
    if($t) $url.="?".$t;
    $ch = curl_init();
    curl_setopt($ch,CURLOPT_URL,$url);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch,CURLOPT_HEADER,0);
    $ret = curl_exec($ch);
    curl_close($ch);
    return  $ret;
}

go(function(){
    echo getHtml('t=启东').PHP_EOL;
});

go(function(){
    echo getHtml().PHP_EOL;
});

结果、是阻塞的。

图

用socket客服端的方式(协程)

因为在支持里面有socket那么添加runtime参数

Swoole\Runtime::enableCoroutine(true);

function getHtml2($t=false){
    $fp=stream_socket_client("tcp://127.0.0.1:81");
    $path="/";
    if($t) $path.="?".$t;
    fwrite($fp, "GET $path HTTP/1.0\r\nAccept: */*\r\n\r\n");
    $ret="";
    while (!feof($fp)) {
        $ret.=fgets($fp, 1024);
    }
    fclose($fp);
    return $ret;
}

go(function(){
    var_dump(getHtml2('t=启东'));
});

go(function(){
    var_dump(getHtml2());
});

输出结果、先出Guest。后输出了启东。这里协程的作用就明显的突出来了

图

如果官方自带支持的协程则不用声明Swoole\Runtime::enableCoroutine(true);

use Swoole\Coroutine\Http\Client as httpClient;
function getHtml2($t=null){
    $client=new httpClient('127.0.0.1', 81);
    $client->get("/?$t");
    echo $client->body;
    $client->close();
}
go(function(){
    getHtml2('t=启东');
});
go(function(){
    getHtml2();
});

多协程控制顺序的基本方法

原来的协程代码。后面结果先输出了

use  Swoole\Coroutine as co;
function query(array $sqls){
    $mysql=new co\MySQL();
    $conn=$mysql->connect(['host'=>"127.0.0.1",'user'=>"root"
    ,'password'=>123456,'database'=>'sk']);
    foreach($sqls as $sql){
        $statement=$mysql->prepare($sql);
        $rows=$statement->execute();

        foreach($rows as $row){
            foreach ($row as $k=>$v){
                echo $k."=>".$v.";";
            }
        }
    }
    echo PHP_EOL;
}
go(function(){
    query(['select sleep(2);','select id,uname from sk_admin where id=1']);
});
go(function(){
    query(['select id,uname from sk_admin where id=2']);
});

echo "done".PHP_EOL;

图

更改同步控制

go(function(){
    $chan=new co\Channel(2);
    go(function()use($chan){
        query(['select id,uname from sk_admin where id=1']);
        $chan->push(1);
    });
    go(function()use($chan){
        query(['select id,uname from sk_admin where id=2']);
        $chan->push(2);
    });
    for($i=0;$i&lt;2;$i++){
        $chan->pop();//会同步阻塞。

    }
    echo "done".PHP_EOL;
});

图

协程defer的使用

WaitGroup.php


use Swoole\Coroutine\Channel;

class WaitGroup{
    private $chan;
    private $count;
    function __construct()
    {
        $this->chan=new Channel(1);
        $this->count=0;
    }
    public function Add(int $c){
        $this->count+=$c;
    }
    public function Done(){
        $this->chan->push(1);
    }

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

go.php

<?php
//swoole4.3.6
require_once "WatiGroup.php";
require_once "vendor\autoload.php";
use  Swoole\Coroutine as co;
function query(array $sqls){
    $mysql=new co\MySQL();
    $conn=$mysql->connect(['host'=>"192.168.123.196",'user'=>"root"
    ,'password'=>'123456','database'=>'sk']);
    foreach($sqls as $sql){
        $statement=$mysql->prepare($sql);
        $rows=$statement->execute();

        foreach($rows as $row){
            foreach ($row as $k=>$v){
                echo $k."=>".$v.";";
            }
        }
    }
    echo PHP_EOL;
}

go(function(){
    $wg=new WaitGroup();
    $wg->Add(2);//设置协程的数量
    $logger=new Monolog\Logger('mysql');
    go(function()use($wg,$logger){
        defer(function ()use($wg,$logger) {
            //用于释放协程资源。和异常补救测试。
            $logger->info("延迟执行UID:". Co::getuid(),['id'=>1,'sleep'=>2]);
            $wg->Done();
        });
        query(['select sleep(2)','select id,uname from sk_admin where id=1']);
        throw new Exception("故意报出的异常、我要出现下面不执行");
        echo "我不执行了";
    });
    go(function()use($wg,$logger){
        query(['select id,uname from sk_admin where id=2']);
        $logger->info("正常执行",['id'=>1,'sleep'=>0]);
        $wg->Done();
    });

    $wg->Wait();
    echo "done".PHP_EOL;
});

结果

图

Last modification:January 31, 2020
如果觉得我的文章对你有用,请随意赞赏