二十二、Swoole 基础学习笔记 - Swoole 进程间无锁计数器 Atomic
hi,我是温新,一名PHPer
文章基于 Swoole 5.0.1 版本编写。
**学习目标:学习并了解 atomic 的使用 **
说明:本篇文章结合官方文档编写及参考网络资料编写,虽非全部原创,但也是结合了自己的理解,若转载请附带本文 URL,编写不易,持续编写更不易,谢谢!
Atomic
是 Swoole 底层提供的原子计数操作类,可以方便整数的无锁原子增减。
- 使用共享内存,可以在不同的进程之间操作计数;
- 基于
gcc/clang
提供的 CPU 原子指令,无需加锁; - 在服务服务器中必须在
$server->start()
前创建才能在Worker
进程中使用; - 默认使用 32 位无符号类型,如需要 64 位有符号整型,可以使用
Swoole\Atomic\Long
。
注意:
- 不要在 onReceive 等回调函数中创建计数器,否则内存会持续增长,造成内存泄露;
- 支持 64 位有符号长整型原子计数,需要使用
new Swoole\Atomic\Long
来创建;Atomic\Long
不支持wait
和wakeup
方法。
创建一个 Atomic
<?php
// 22-swoole-atomic.php
// 创建一个原子计数对象实例
$atomic = new Swoole\Atomic();
// 增加计数
$atomic->add(10);
// 获取当前计数的值
echo $atomic->get() . PHP_EOL;
__construct()
含义:构造函数。创建一个原子计数对象。
Swoole\Atomic::__construct(int $init_value = 0);
参数:
- init_value:指定初始化的数值。
-
Atomic
只能操作32
位无符号整数,最大支持42
亿,不支持负数; - 在Server
中使用原子计数器,必须在Server->start
前创建; - 在 Process 中使用原子计数器,必须在Process->start
前创建。
关于 add、get、sub、set、cmpset 等这些方请参考官方文档。下面学习 wait 与 wakeup 这两个方法的使用。
wait 与 waitup
pcntl_fork
了解这两个函数之前,先来一个案例:
<?php
// 22-swoole-atomic-wait.php
if (pcntl_fork() > 0) {
echo '父进程启动' . PHP_EOL;
sleep(1);
echo '父亲进程执行结束' . PHP_EOL;
} else {
echo '子进程启动' . PHP_EOL;
sleep(1);
echo '子进程执行结束' . PHP_EOL;
}
再来看看执行结果
$php 22-swoole-atomic-wait.php
父进程启动
子进程启动
子进程执行结束
父亲进程执行结束
$php 22-swoole-atomic-wait.php
父进程启动
子进程启动
父亲进程执行结束
子进程执行结束
父进程与子进程在执行任务时,哪个先执行完成这是没法确定的,下面就使用 wait 来等待。
wait()
<?php
// 22-swoole-atomic-wait-1.php
$atomic = new Swoole\Atomic();
if (pcntl_fork() > 0) {
echo '父进程启动' . PHP_EOL;
// 等待 5 秒继续执行
$atomic->wait(5);
echo '父亲进程执行结束' . PHP_EOL;
} else {
echo '子进程启动' . PHP_EOL;
echo '子进程执行结束' . PHP_EOL;
}
执行结果
$php 22-swoole-atomic-wait-1.php
父进程启动
子进程启动
子进程执行结束
# 等待了 5 秒
父亲进程执行结束
wakeup
<?php
// 22-swoole-atomic-wait-2.php
$atomic = new Swoole\Atomic();
if (pcntl_fork() > 0) {
echo '父进程启动' . PHP_EOL;
// 设置 wait 状态
$atomic->wait(5);
echo '父亲进程执行结束' . PHP_EOL;
} else {
echo '子进程启动' . PHP_EOL;
// 唤醒处于 wait 状态的进程
a
echo '子进程执行结束' . PHP_EOL;
}
代码解释:
- 1、在父进程中设置了 wait 状态,当进程执行到该代码时,会进入 wait 状态,状态没有被清除时,后面的代码不会被执行;
- 2、父进程 fork 出子进程后,当子进程执行代码时,执行到
$atomic-wakeup()
时,处于 wait 状态的进程会被唤醒;
输出结果如下:
$php 22-swoole-atomic-wait-2.php
父进程启动
子进程启动
子进程执行结束
父亲进程执行结束
Atomic 测试
<?php
// 22-swoole-atomic-test.php
//
$atomic = new Swoole\Atomic();
$pool = new Swoole\Process\Pool(3, SWOOLE_IPC_NONE, 0, true);
$pool->on('Workerstart', function ($pool, $workerId) use ($atomic) {
Swoole\Timer::tick(1000, function () use ($atomic, $workerId) {
echo '工作进程 ' . $workerId . ':' . '原子计数' . $atomic->get() . PHP_EOL;
$atomic->add(1);
});
});
$pool->on('WorkerStop', function ($pool, $workerId) {
echo $workerId . ' Stop' . PHP_EOL;
});
$pool->start();
输出结果
$php 22-swoole-atomic-test.php
工作进程 2 : 原子计数 0
工作进程 1 : 原子计数 0
工作进程 0 : 原子计数 2
工作进程 0 : 原子计数 3
工作进程 1 : 原子计数 3
工作进程 2 : 原子计数 5
工作进程 1 : 原子计数 6
工作进程 0 : 原子计数 6
工作进程 2 : 原子计数 8
从输出结果看出问题了吗?
我是温新,下篇文章继续学习。
请登录后再评论