- 使用共享内存,可以在不同的进程之间操作计数
- 基于gcc/clang提供的CPU原子指令,无需加锁
- 在服务器程序中必须在Server->start前创建才能在Worker进程中使用
- 默认使用32位无符号类型,如需要64有符号整型,可使用Swoole\Atomic\Long
- 勿在onReceive等回调函数中创建计数器,否则内存会持续增长,造成内存泄漏
- Swoole\Atomic\Long不支持wait和wakeup方法
计数器示例
use Swoole\Process;
use Swoole\Atomic;
// 初始化一个指定初始值的计数器,不支持负数
$atomic = new Atomic(20);
$worker1 = new Process(function ($process) use ($atomic) {
while ($atomic->get() >= 10) {
// 减少指定计数,返回计数后的结果,如果减少后为负数,那么将直接变为最大值;不能为负数
echo '减少:' . $result = $atomic->sub(1) . PHP_EOL;
}
});
$worker1->start();
$worker2 = new Process(function ($process) use ($atomic) {
while ($atomic->get() >= 0 && $atomic->get() <= 30 ) {
// 增加指定计数,返回计数后的结果,如果超过42亿,那么高位值将会溢出被丢弃
echo '增加:' . $result = $atomic->add(random_int(1, 2)). PHP_EOL;
}
});
$worker2->start();
while ($status = Process::wait(true)) {
echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL;
}
echo '当前值为:' . $atomic->get() . PHP_EOL;
// 将计数器设置为指定的值
$atomic->set($value = 100);
// 如果计数器的值为$cmp_value,那么将被设置为$set_value
var_dump('是否成功:' . $atomic->cmpset($cmp_value = 100, $set_value = 200));
计数器充当锁
use Swoole\Process;
use Swoole\Atomic;
// 初始化一个指定初始值的计数器,不支持负数
$atomic = new Atomic(0);
$worker1 = new Process(function ($process) use ($atomic) {
echo "{$process->pid}:进入等待" . PHP_EOL;
// 计数器等待指定秒数,-1表示永不超时;超时时返回false,错误码为EAGAIN;原子计数的值为0时程序进入等待状态,为1时直接返回true,其他值无法正常使用
if (!$atomic->wait(10)) {
echo '等待错误:' . swoole_strerror(swoole_errno()) . PHP_EOL;
}
echo "{$process->pid}:被唤醒" . PHP_EOL;
});
$worker1->start();
$worker2 = new Process(function ($process) use ($atomic) {
sleep(2);
// 唤醒指定数量的其他进程;计数为0时直接返回true,为1时唤醒后返回true,唤醒后计数为0
if (!$atomic->wakeup($n = 2)) {
echo '唤醒错误:' . swoole_strerror(swoole_errno()) . PHP_EOL;
}
echo '唤醒' . PHP_EOL;
});
$worker2->start();
while ($status = Process::wait(true)) {
echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL;
}