zoukankan      html  css  js  c++  java
  • Swoole从入门到入土(25)——多进程[进程间无锁计数器]

    Atomic 是 Swoole 底层提供的原子计数操作类,可以方便整数的无锁原子增减。原子计数器有如下特点:

    - 使用共享内存,可以在不同的进程之间操作计数

    - 基于 gcc/clang 提供的 CPU 原子指令,无需加锁

    - 在服务器程序中必须在 Server->start 前创建才能在 Worker 进程中使用

    - 默认使用 32 位无符号类型,如需要 64 有符号整型,可使用 SwooleAtomicLong

    注意:

    - 请勿在 onReceive 等回调函数中创建计数器,否则内存会持续增长,造成内存泄漏。

    - 支持 64 位有符号长整型原子计数,需要使用 new SwooleAtomicLong 来创建。AtomicLong 不支持 wait 和 wakeup 方法。

    完整代码示例:

    $atomic = new SwooleAtomic();
    
    $serv = new SwooleServer('127.0.0.1', '9501');
    $serv->set([
        'worker_num' => 1,
        'log_file' => '/dev/null'
    ]);
    $serv->on("start", function ($serv) use ($atomic) {
        if ($atomic->add() == 2) {
            $serv->shutdown();
        }
    });
    $serv->on("ManagerStart", function ($serv) use ($atomic) {
        if ($atomic->add() == 2) {
            $serv->shutdown();
        }
    });
    $serv->on("ManagerStop", function ($serv) {
        echo "shutdown
    ";
    });
    $serv->on("Receive", function () {
    
    });
    $serv->start();

    成员函数

    1) __construct():构造函数。创建一个原子计数对象。

    SwooleAtomic::__construct(int $init_value = 0);

    $init_value:指定初始化的数值

    注意:

    - Atomic 只能操作 32 位无符号整数,最大支持 42 亿,不支持负数;

    - 在 Server 中使用原子计数器,必须在 Server->start 前创建;

    - 在 Process 中使用原子计数器,必须在 Process->start 前创建。

    2) add():增加计数

    SwooleAtomic->add(int $add_value = 1): int

    $add_value:要增加的数值【必须为正整数】

    返回值:add 方法操作成功后返回结果数值

    注意:与原值相加如果超过 42 亿,将会溢出,高位数值会被丢弃。

    3) sub():减少计数

    SwooleAtomic->sub(int $sub_value = 1): int

    $sub_value:要减少的数值【必须为正整数】

    返回值:sub 方法操作成功后返回结果数值

    注意:与原值相减如果低于 0 将会溢出,高位数值会被丢弃。

    4) get():获取当前计数的值

    SwooleAtomic->get(): int

    返回值:返回当前的数值

    5) set():将当前值设置为指定的数字。

    SwooleAtomic->set(int $value): void

    $value:指定要设置的目标数值

    6) compset():如果当前数值等于参数 1,则将当前数值设置为参数 2。

    SwooleAtomic->cmpset(int $cmp_value, int $set_value): bool

    $cmp_value:如果当前数值等于 $cmp_value 返回 true,并将当前数值设置为 $set_value,如果不等于返回 false【必须为小于 42 亿的整数】

    $set_value:如果当前数值等于 $cmp_value 返回 true,并将当前数值设置为 $set_value,如果不等于返回 false【必须为小于 42 亿的整数】

    7) wait():设置为 wait 状态。

    SwooleAtomic->wait(float $timeout = 1.0): bool

    $timeout:指定超时时间【设置为 -1 时表示永不超时,会持续等待直到有其他进程唤醒】,单位:秒【支持浮点型,如 1.5 表示 1s+500ms】

    返回值:超时返回 false,错误码为 EAGAIN,可使用 swoole_errno 函数获取;成功返回 true,表示有其他进程通过 wakeup 成功唤醒了当前的锁

    注意:

    - 在协程环境下,wait 会阻塞整个进程而不是协程,因此请勿在协程环境中使用 Atomic->wait() 避免引起进程挂起。

    - 使用 wait/wakeup 特性时,原子计数的值只能为 0 或 1,否则会导致无法正常使用;

    - 当然原子计数的值为 1 时,表示不需要进入等待状态,资源当前就是可用。wait 函数会立即返回 true。 

    说明:当原子计数的值为 0 时程序进入等待状态。另外一个进程调用 wakeup 可以再次唤醒程序。底层基于 Linux Futex 实现,使用此特性,可以仅用 4 字节内存实现一个等待、通知、锁的功能。在不支持 Futex 的平台下,底层会使用循环 usleep(1000) 模拟实现。

    示例:

    $n = new SwooleAtomic;
    if (pcntl_fork() > 0) {
        echo "master start
    ";
        $n->wait(1.5);
        echo "master end
    ";
    } else {
        echo "child start
    ";
        sleep(1);
        $n->wakeup();
        echo "child end
    ";
    }

    8) wakeup():唤醒处于 wait 状态的其他进程。

    SwooleAtomic->wakeup(int $n = 1): bool

    $n:唤醒的进程数量

    注意:

    -  当前原子计数如果为 0 时,表示没有进程正在 wait,wakeup 会立即返回 true;

    -  当前原子计数如果为 1 时,表示当前有进程正在 wait,wakeup 会唤醒等待的进程,并返回 true;

    -  被唤醒的进程返回后,会将原子计数设置为 0,这时可以再次调用 wakeup 唤醒其他正在 wait 的进程。

    写在最后:经笔者测试,SwooleAtomic原子操作并非完全稳定,会出现更新丢失,如下所示:

    $atomic=new SwooleAtomic();
    $pool=new SwooleProcessPool(3,SWOOLE_IPC_NONE,0,true);
    $pool->on("WorkerStart",function($pool,$worker_id) use($atomic){
    
        SwooleTimer::tick(1000,function() use ($atomic,$worker_id){
            echo $worker_id,":",$atomic->get(),PHP_EOL;
            $atomic->add(1);
        });
    
    });
    $pool->on("WorkerStop",function($pool,$worker_id){
        echo "$worker_id stop",PHP_EOL;
    });
    
    $pool->start();

    ---------------------------  我是可爱的分割线  ----------------------------

    最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

  • 相关阅读:
    动态横向(水平)合并Repeater数据行DataItem的列
    动态绑数据(Repeater控件HeaderTemplate和ItemTemplate)
    动态横向(水平)合并GridView数据行DataRow的列
    动态绑数据(GridView控件Header和ItemTemplate)
    用具体列名替代星号
    如何实现数据行转换列显示
    用LINQ获取XML节点数据
    从字符串中获取XML节点数据
    字符串创建XML文档
    根据Attribute值条件对XML文档进行修改
  • 原文地址:https://www.cnblogs.com/ddcoder/p/14265269.html
Copyright © 2011-2022 走看看