乐趣区

swoole协程之channel

前言
通过 swoole 协程入门,了解到协程的基本写法。更准确一点,是独立、无执行顺序的任务。那有依赖关系或者执行顺序有关的任务怎么办呢?靠 channel 了!
Channel 特点
与容量有关如果 channel 未满,push 不阻塞,如果已满,push 让出控制流;如果 channel 为空,pop 让出控制流
看例子:depend_co.php
<?php
// 设置一个容量为 50 的 channel
$chan = new \Swoole\Coroutine\Channel(50);
function t4(\Swoole\Coroutine\Channel $chan) {
Co::sleep(0.005); #1
$chan->push([__METHOD__=>__LINE__]); #2
}

function t5(\Swoole\Coroutine\Channel $chan) {
Co::sleep(0.005); #3
$chan->push([__METHOD__=>__LINE__]); #4
}

function t6(\Swoole\Coroutine\Channel $chan) {
Co::sleep(0.005); #5
$chan->push([__METHOD__=>__LINE__]); #6
}
go(“t4”, $chan);
go(“t5”, $chan);
go(“t6”, $chan);

// cousume 协程:c1
go(function() use($chan) {
// chan 元素个数
$chanNum = 3;
// chan 有数据时
while($chanNum>0) {#7
$item = $chan->pop(); #8
var_dump($item); #9
$chanNum –;
}
});
分析
3 个生产者协程 (t4/t5/t6),1 个消费者协程(用 c1 描述)#1 t4 遇到 Co::sleep,让出控制流,挂起;#3 t5,类似于 t4,挂起 #5 t6, 类似于 t4,挂起 #7 c1 开始执行,while 循环为真,执行 channel::pop()#8 可能情况:channel 为空,c1 让出控制流,挂起;#8 可能情况:channel 非空,pop 弹出数据,while 循环继续如果 while 循环为假,c1 执行结束如果 while 循环为真,进入 channel::pop() 流程 …5ms 后 …t4 恢复执行(t4/t5/t6 sleep 时间相同,因此都有可能先恢复执行,但同时只能有一个恢复,为描述简单以 t4 为例)#2 t4 写入 channel 数据,此时 channel 非空 #8 如果有消费者协程,控制流发生变化,消费者协程 c1 恢复执行(思考:如果 c1 的 while 循环为假已经结束,会发生什么呢?)c1 协程运行直到让出控制流或者结束;控制流回到 t4 协程,t4 协程没有后续代码,执行结束;(问题:控制流有可能回到其他协程 t5/t6 嘛?还是一定会回到 t4 协程)t5(t6)恢复执行,流程类似于 t4 的执行流程
注意事项
channel 的容量很重要,过小的容量导致生产者自动让出控制流而不能执行;消费者,需要判断生产者个数,来确定循环次数或循环结束边界,如果判断错误(太小,导致 channel 数据未消费;太大,消费者会让出控制流),会带来意外的结果。
总结
回到文章的开头,好像我们是想介绍有依赖关系、调用顺序的任务怎么写?你肯定已经猜到了,那就是先导任务放在生产者协程,后续任务放在消费者协程通过 channel 的机制,保障任务的先后执行顺序
channel 解决了协程间通信的问题,同时也提供了一种任务调度的方式。

退出移动版