原文首发链接:Swoole 源码分析之 Channel 通道模块
大家好,我是码农先森。
引言
通道,用于协程间通讯,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。
通道与 PHP 的 Array 类似,仅占用内存,没有其他额外的资源申请,所有操作均为内存操作,无 IO 消耗。
底层使用 PHP 引用计数实现,无内存拷贝。即使是传递巨大字符串或数组也不会产生额外性能消耗 channel 基于引用计数实现,是零拷贝的。
源码拆解
Channel
通道需要在协程环境中使用,我们先看下面这段代码,使用 new Channel(1)
创建一个 channel 对象,然后在第一个协程中向通道中推送数据,在第二个协程获取到通道内的数据进行消费。
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use function Swoole\Coroutine\run;
run(function(){
// 创建 channel 通道对象
$channel = new Channel(1);
Coroutine::create(function () use ($channel) {
for($i = 0; $i < 10; $i++) {
Coroutine::sleep(1.0);
// 向通道内推送数据
$channel->push(['rand' => rand(1000, 9999), 'index' => $i]);
echo "{$i}\n";
}
});
Coroutine::create(function () use ($channel) {
while(1) {
// 从通道中获取数据
$data = $channel->pop(2.0);
if ($data) {
var_dump($data);
} else {
assert($channel->errCode === SWOOLE_CHANNEL_TIMEOUT);
break;
}
}
});
});
在分析源代码之前,我们可以提前看一下源码整体的调用逻辑图,以便我们有个大致的印象。
这段代码主要是在 Swoole 的协程环境中创建 Channel 对象并初始化其容量的逻辑。
// swoole-src/ext-src/swoole-channel.cc:132
static PHP_METHOD(swoole_channel_coro, __construct) {
zend_long capacity = 1;
// 解析传入的参数
ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(capacity)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);