php Swoole进程同步计数器

2021-10-03
  1. 使用共享内存,可以在不同的进程之间操作计数
  2. 基于gcc/clang提供的CPU原子指令,无需加锁
  3. 在服务器程序中必须在Server->start前创建才能在Worker进程中使用
  4. 默认使用32位无符号类型,如需要64有符号整型,可使用Swoole\Atomic\Long
  5. 勿在onReceive等回调函数中创建计数器,否则内存会持续增长,造成内存泄漏
  6. 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;
}

 

{/if}