共计 1100 个字符,预计需要花费 3 分钟才能阅读完成。
前言
channel 用于进程内跨协程通讯,按照角色分为生产协程和消费协程。生产协程,在 channel 已满时,会被挂起;消费协程,在 channel 为空是,也会被挂起。
看例子
<?php
$chan = new \Swoole\Coroutine\Channel(50);
function t4(\Swoole\Coroutine\Channel $chan)
{
Co::sleep(0.005);
$chan->push([__METHOD__ => __LINE__]);
}
function t5(\Swoole\Coroutine\Channel $chan)
{
Co::sleep(0.005);
$chan->push([__METHOD__ => __LINE__]);
}
go(“t4”, $chan);
go(“t5”, $chan);
go(function () use ($chan) {
// chan 元素个数
$chanNum = 1;
while ($chanNum > 0) {
$item = $chan->pop();
var_dump($item);
$chanNum–;
}
});
分析
上面的例子,如果赋值 $chanNum=1,会导致 channel 中有数据未被消费;如果赋值 $chanNum=3,由于 channel 数据不足,消费协程会挂起,程序无法正常退出。
准确设置 channel 元素个数,是很重要的事。实践中,有些场景无法预测 channel 元素个数 (例如请求第三方接口,如果有数据则 push 到 channel,无数据则不 push),那有什么解决办法嘛?有!
保证生产者协程不挂起的前提下,在 php 的 register_shutdown_function() 函数中,去实现未完成的消费者功能
<?php
register_shutdown_function(function() use ($chan) {
go(function()use($chan){
$queue_num = $chan->stats()[“queue_num”];
for($i=0;$i<$queue_num;$i++) {
var_dump($chan->pop());
}
});
});
这个办法能解决问题,但是显然不是那么优雅
消费者函数要重复写一遍,由于 swoole 协程的语法,无法复用这个问题,应该在 swoole 协程层面来处理,在 register_shutdown_function() 中处理,也只是临时解决办法
总结
swoole 协程的 push/pop 机制,决定了需要设置一个合理的 channel 元素个数。实践中某些场景,又无法准确评估这个值,只能用临时办法解决,希望 swoole 能提供更优雅的解决方式。