首先翻到官网https://wiki.swoole.com/#/coroutine/channel。
有关channel:通道,用于协程间通讯,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。
其构造方法:SwooleCoroutineChannel->__construct(int $capacity = 1),有个capacity的容量参数,一开始并不理解, 敲点代码尝试下,理解起来容易许多。
[root@guangzhou coroutine]# cat coroutine_channel.php <?php #开启一键协程 Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); Co un(function(){ // 设置一个容量为1的通道 $chan = new SwooleCoroutineChannel(1); SwooleCoroutine::create(function () use ($chan) { for($i = 0; $i < 6; $i++) { $chan->push( date('H:i:s') . " 数据 i:" . $i . " "); echo date('H:i:s') . " 第{$i}次塞数据时间 "; } }); SwooleCoroutine::create(function () use ($chan) { while(1) { $data = $chan->pop(); if($data){ echo date('H:i:s') . " 取数据时间 "; echo $data . " "; Co::sleep(2); }else{ break; } } }); });
//看到这段代码,你认为他的输出情况是怎样,先自己思考下!!
上面脚本运行后每次pop后获取通道数据后休眠2秒,是协程模式,可能也会想当然认为每次取到数据的间隔应该是2秒??
现在运行脚本:
[root@guangzhou coroutine]# php coroutine_channel.php 06:55:32 第0次塞数据时间 06:55:32 第1次塞数据时间 06:55:32 取数据时间 06:55:32 数据 i:0 06:55:34 第2次塞数据时间 06:55:34 取数据时间 06:55:32 数据 i:1 06:55:36 第3次塞数据时间 06:55:36 取数据时间 06:55:32 数据 i:2 06:55:38 第4次塞数据时间 06:55:38 取数据时间 06:55:34 数据 i:3 06:55:40 第5次塞数据时间 06:55:40 取数据时间 06:55:36 数据 i:4 06:55:42 取数据时间 06:55:38 数据 i:5
有没有发现前三次取数据时间点都是2秒的,第四次的数据一下子到了4秒。
经过swoole官方群的高人指点,给出了正确的执行路径如下图(数字从小到大是执行顺序):
因为设置的capacity=1,导致第二次的push未成功,需要先pop方能继续下去。
第一次pop后,回到第三次push循环,程序判断管道已满这时将处理第一次pop的数据,又开始第四次push循环,程序判断管道又满了,pop第二次push的数据。
到目前为止前三次push的数据并未进入到sleep这里,导致后续打印第一次pop数据时,塞数据时间点比取数据时间点被推后了。
从第三次数据pop后的数据都要经历push失败->echo+sleep->pop上一次的,这里就会有休眠2秒的时间加上pop上次时间就是4秒了。
这里有点绕,需要多思考并动手实践。
修改capacity=10,并push循环7次,运行程序:
[root@guangzhou coroutine]# cat coroutine_channel.php <?php #开启一键协程 Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); Co un(function(){ // 设置一个容量为1的通道 $chan = new SwooleCoroutineChannel(10); SwooleCoroutine::create(function () use ($chan) { for($i = 0; $i < 7; $i++) { $chan->push( date('H:i:s') . " 数据 i:" . $i . " "); echo date('H:i:s') . " 第{$i}次塞数据时间 "; } }); SwooleCoroutine::create(function () use ($chan) { while(1) { $data = $chan->pop(); if($data){ echo date('H:i:s') . " 取数据时间 "; echo $data . " "; Co::sleep(2); }else{ break; } } }); });
执行脚本:
[root@guangzhou coroutine]# php coroutine_channel.php 07:29:22 第0次塞数据时间 07:29:22 第1次塞数据时间 07:29:22 第2次塞数据时间 07:29:22 第3次塞数据时间 07:29:22 第4次塞数据时间 07:29:22 第5次塞数据时间 07:29:22 第6次塞数据时间 07:29:22 取数据时间 07:29:22 数据 i:0 07:29:24 取数据时间 07:29:22 数据 i:1 07:29:26 取数据时间 07:29:22 数据 i:2 07:29:28 取数据时间 07:29:22 数据 i:3 07:29:30 取数据时间 07:29:22 数据 i:4 07:29:32 取数据时间 07:29:22 数据 i:5 07:29:34 取数据时间 07:29:22 数据 i:6
发现所有 i:xxx 的时间点几乎一致。
结论即是capacity的作用是限制管道写入的长度,如果超出写入长度限制则会写入失败,等数据pop后数据量少于capacity等数值才能push管道写入成功。