三十四、Swoole 基础学习笔记 - Swoole Channel 协程间通信

作者: 温新

分类: 【Swoole 系列】

阅读: 2140

时间: 2023-03-13 12:22:43

hi,我是温新,一名PHPer

文章基于 Swoole 5.0.1 版本编写。

学习目标:了解协程间通信

说明:本篇文章结合官方文档编写及参考网络资料编写,虽非全部原创,但也是结合了自己的理解,若转载请附带本文 URL,编写不易,持续编写更不易,谢谢!

通道,用于协程间通讯,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。

什么是 Channel

Channel 可以理解为消息队列,只不过是协程间的消息队列,多个协程通过 pushpop 操作队列中的生产消息和消费消息,用来发送或者接收数据进行协程之间的通讯。需要注意的是 Channel 是没法跨进程的,只能一个 Swoole 进程里的协程间通讯,最典型的应用是连接池和并发调用。

实现原理

  • 通道与 PHPArray 类似,仅占用内存,没有其他额外的资源申请,所有操作均为内存操作,无 IO 消耗
  • 底层使用 PHP 引用计数实现,无内存拷贝。即使是传递巨大字符串或数组也不会产生额外性能消耗
  • channel 基于引用计数实现,是零拷贝的

通信案例

Swoole\Coroutine\run(function () {
    // 实例化通道
    $channel = new Swoole\Coroutine\Channel(1);

    // 这个协程负责向通道中写入数据
    Swoole\Coroutine::create(function() use ($channel) {
        for ($i = 0; $i < 10; $i++) {
            Swoole\Coroutine::sleep(1);
            // 通道中写入数据
            $channel->push([
                'rand' => rand(1000, 9999),
                'index' => $i,
            ]);
            echo $i . PHP_EOL;
        }
    });

    
    
    // 这个协程负责从通道中读取数据
    Swoole\Coroutine::create(function () use ($channel) {
        while (true) {
            $data = $channel->pop(2);
            if ($data) {
          
                print_r($data);
            } else {
                assert($channel->errCode === SWOOLE_CHANNEL_TIMEOUT);
                break;
            }
        }
    });
});

API 学习

__construct

含义:通道构造方法。

语法:

Swoole\Coroutine\Channel::__construct(int $capacity = 1)

# 参数
$capacity:设置容量 【必须为大于或等于 1 的整数】

底层使用 PHP 引用计数来保存变量,缓存区只需要占用 $capacity * sizeof(zval) 字节的内存,PHP7 版本下 zval16 字节,如 $capacity = 1024 时,Channel 最大将占用 16K 内存

push

含义:向通道中写入数据。

语法:

Swoole\Coroutine\Channel->push(mixed $data, float $timeout = -1): bool
    
# 参数
$data:push 数据 【可以是任意类型的 PHP 变量,包括匿名函数和资源】;
$timeout:超时时间。

在通道已满的情况下,push 会挂起当前协程,在约定的时间内,如果没有任何消费者消费数据,将发生超时,底层会恢复当前协程,push 调用立即返回 false,写入失败

扩展:

通道已满:

​ 1、自动 yield 当前协程,其他消费者协程 pop 消费数据后,通道可写,将重新 resume 当前协程;

​ 2、多个生产者协程同时 push 时,底层自动进行排队,底层会按照顺序逐个 resume 这些生产者协程。

通道为空:

​ 1、自动唤醒其中一个消费者协程;

​ 2、多个消费者协程同时 pop 时,底层自动进行排队,按照顺序逐个 resume 这些消费者协程。

pop

含义:从通道中读取数据。

语法:Swoole\Coroutine\Channel->pop(float $timeout = -1): mixed

扩展

通道已满

​ 1、pop 消费数据后,将自动唤醒其中一个生产者协程,让其写入新数据;

​ 2、多个生产者协程同时 push 时,底层自动进行排队,按照顺序逐个 resume 这些生产者协程。

通道为空

​ 1、自动 yield 当前协程,其他生产者协程 push 生产数据后,通道可读,将重新 resume 当前协程;

​ 2、多个消费者协程同时 pop 时,底层自动进行排队,底层会按照顺序逐个 resume 这些消费者协程。

stats

含义:获取通道的状态。

语法:Swoole\Coroutine\Channel->stats(): array

返回值:

  • 返回一个数组,缓冲通道将包括 4 项信息,无缓冲通道返回 2 项信息
    • consumer_num 消费者数量,表示当前通道为空,有 N 个协程正在等待其他协程调用 push 方法生产数据
    • producer_num 生产者数量,表示当前通道已满,有 N 个协程正在等待其他协程调用 pop 方法消费数据
    • queue_num 通道中的元素数量

close

含义:关闭通道。并唤醒所有等待读写的协程。

语法:Swoole\Coroutine\Channel->close(): bool

length

含义:获取通道中的元素数量。

语法:Swoole\Coroutine\Channel->length(): int

isEmpty

含义:判断当前通道是否为空。

语法:Swoole\Coroutine\Channel->isEmpty(): bool

isFUll

含义:判断当前通道是否已满。

语法:Swoole\Coroutine\Channel->isFull(): bool

我是温新,本篇文章结束。

请登录后再评论