• [转]信号量---进程间通信 ---php版


    原文:https://www.cnblogs.com/nickbai/articles/6133354.html

    -------------

    首先我们来讲解一下,php如何实现共享内存。(注意:本示例是在linux下,请勿在windows下尝试此代码,并且必须是在php-cli模式下)

      php提供了两种实现共享内存的扩展。下面我们来一一讲解。

      一、shmop 系类函数

    复制代码
     1 <?php
     2 /**
     3  * author: NickBai
     4  * createTime: 2016/12/5 0005 上午 9:17
     5  */
     6 $shm_key = ftok(__FILE__, 't');
     7 
     8 /**
     9 开辟一块共享内存
    10 
    11 int $key , string $flags , int $mode , int $size
    12 $flags: a:访问只读内存段
    13 c:创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写
    14 w:可读写的内存段
    15 n:创建一个新内存段,如果该内存段已存在,则会失败
    16 $mode: 八进制格式  0655
    17 $size: 开辟的数据大小 字节
    18 
    19  */
    20 
    21 $shm_id = shmop_open($shm_key, "c", 0655, 1024);
    22 
    23 /**
    24  * 写入数据 数据必须是字符串格式 , 最后一个指偏移量
    25  * 注意:偏移量必须在指定的范围之内,否则写入不了
    26  *
    27  */
    28 $size = shmop_write($shm_id, 'hello world', 0);
    29 echo "write into {$size}";
    30 
    31 #读取的范围也必须在申请的内存范围之内,否则失败
    32 $data = shmop_read($shm_id, 0, 100);
    33 var_dump($data);
    34 
    35 #删除 只是做一个删除标志位,同时不在允许新的进程进程读取,当在没有任何进程读取时系统会自动删除
    36 shmop_delete($shm_id);
    37 
    38 #关闭该内存段
    39 shmop_close($shm_id);
    复制代码

      注意两点:

      1、shmop_read 函数 第2个参数 是读取的起始位置,第3个参数是要读取的长度,如果你要读取的长度小于信息长度,原信息会被截断成你指定的长度。

      2、shmop_write 函数 仅可写 字符串 内容!

      二、用 Semaphore 扩展中的 sem 类函数 (用起来更方便,类似 key-value 格式)

    复制代码
    <?php
    /**
     * author: NickBai
     * createTime: 2016/12/5 0005 上午 9:28
     */
    // Get the file token key
    $key = ftok(__FILE__, 'a');
    $shar_key = 1;
    
    // 创建一个共享内存
    $shm_id = shm_attach($key, 1024, 0666); // resource type
    if ($shm_id === false) {
        die('Unable to create the shared memory segment' . PHP_EOL);
    }
    
    #设置一个值
    shm_put_var($shm_id, $shar_key, 'test');
    
    #删除一个key
    //shm_remove_var($shm_id, $shar_key);
    
    #获取一个值
    $value = shm_get_var($shm_id,  $shar_key);
    var_dump($value);
    
    #检测一个key是否存在
    // var_dump(shm_has_var($shm_id,  $shar_key));
    
    #从系统中移除
    shm_remove($shm_id);
    
    #关闭和共享内存的连接
    shm_detach($shm_id);
    复制代码

      shm_put_var 第三个参数 写入的值 是一个混合类型,所以没有shmop_write的局限性。

      注意:$shar_key 只能是 int 型的参数。

      介绍完了php如何创建、操作共享内存,下面我们来看一下,他们如何在进程间通信发挥作用吧。

    复制代码
     1 <?php
     2 /**
     3  * author: NickBai
     4  * createTime: 2016/12/5 0005 上午 10:26
     5  */
     6 
     7 //共享内存通信
     8 
     9 //1、创建共享内存区域
    10 $shm_key = ftok(__FILE__, 't');
    11 $shm_id = shm_attach( $shm_key, 1024, 0655 );
    12 const SHARE_KEY = 1;
    13 $childList = [];
    14 
    15 //2、开3个进程 读写 该内存区域
    16 for( $i = 0; $i < 3; $i++ ){
    17 
    18     $pid = pcntl_fork();
    19     if( $pid == -1 ){
    20         exit('fork fail!' . PHP_EOL);
    21     }else if( $pid == 0 ){
    22 
    23         //子进程从共享内存块中读取 写入值 +1 写回
    24         if ( shm_has_var($shm_id, SHARE_KEY) ){
    25             // 有值,加一
    26             $count = shm_get_var($shm_id, SHARE_KEY);
    27             $count ++;
    28             //模拟业务处理逻辑延迟
    29             $sec = rand( 1, 3 );
    30             sleep($sec);
    31 
    32             shm_put_var($shm_id, SHARE_KEY, $count);
    33         }else{
    34             // 无值,初始化
    35             $count = 0;
    36             //模拟业务处理逻辑延迟
    37             $sec = rand( 1, 3 );
    38             sleep($sec);
    39 
    40             shm_put_var($shm_id, SHARE_KEY, $count);
    41         }
    42 
    43         echo "child process " . getmypid() . " is writing ! now count is $count
    ";
    44 
    45         exit( "child process " . getmypid() . " end!
    " );
    46     }else{
    47         $childList[$pid] = 1;
    48     }
    49 }
    50 
    51 // 等待所有子进程结束
    52 while( !empty( $childList ) ){
    53     $childPid = pcntl_wait( $status );
    54     if ( $childPid > 0 ){
    55         unset( $childList[$childPid] );
    56     }
    57 }
    58 
    59 //父进程读取共享内存中的值
    60 $count = shm_get_var($shm_id, SHARE_KEY);
    61 echo "final count is " . $count . PHP_EOL;
    62 
    63 
    64 //3、去除内存共享区域
    65 #从系统中移除
    66 shm_remove($shm_id);
    67 #关闭和共享内存的连接
    68 shm_detach($shm_id);
    复制代码

      逻辑很简单,开启3个进程,对同一个共享内存中的数据进行读写。有一个count的值,如果读到就+1,下面我们看一下运行结果:

      从结果中我们可以看到,最终的 count 的值还是0。这是为什么呢?简单分析一下,不难发现,当我们开启创建进程的时候,3个子进程同时打开了 共享内存区域,此时他们几乎是同步的,所以读到的信息都是没有count值,此时他们执行自己的业务

    逻辑然后将 count 为0的结果写入内存区域。这并不是我们想要的结果,三个子进程互相抢占了资源,这是不合理的,那怎么规避这个问题呢?答案是通过 信号量 !

      信号量

      信号量是什么? 信号量 : 又称为信号灯、旗语 用来解决进程(线程同步的问题),类似于一把锁,访问前获取锁(获取不到则等待),访问后释放锁。

      举一个生活中的例子:以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口

    处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用

      下面我们来看一下信号量的几个函数:

    复制代码
     1 <?php
     2 $key=ftok(__FILE__,'t');
     3 
     4 /**
     5  * 获取一个信号量资源
     6  int $key [, int $max_acquire = 1 [, int $perm = 0666 [, int $auto_release = 1 ]]] 
     7  $max_acquire:最多可以多少个进程同时获取信号
     8  $perm:权限 默认 0666
     9  $auto_release:是否自动释放信号量
    10  */
    11 $sem_id=sem_get($key);
    12 
    13 #获取信号
    14 sem_acquire($seg_id);
    15 
    16 //do something 这里是一个原子性操作
    17 
    18 //释放信号量
    19 sem_release($seg_id);
    20 
    21 //把次信号从系统中移除
    22 sem_remove($sem_id);
    23 
    24 
    25 //可能出现的问题
    26 $fp = sem_get(fileinode(__DIR__), 100);
    27 sem_acquire($fp);
    28 
    29 $fp2 = sem_get(fileinode(__DIR__), 1));
    30 sem_acquire($fp2);
    复制代码

      注释的很详细了,不懂的还可以查看一下手册的介绍。那么我们现在就用信号量来修改我们的方法吧。

    复制代码
     1 <?php
     2 /**
     3  * author: NickBai
     4  * createTime: 2016/12/5 0005 上午 10:26
     5  */
     6 
     7 //共享内存通信
     8 
     9 //1、创建共享内存区域
    10 $shm_key = ftok(__FILE__, 't');
    11 $shm_id = shm_attach( $shm_key, 1024, 0655 );
    12 const SHARE_KEY = 1;
    13 $childList = [];
    14 
    15 //加入信号量
    16 $sem_id = ftok(__FILE__,'s');
    17 $signal = sem_get( $sem_id );
    18 
    19 //2、开3个进程 读写 该内存区域
    20 for( $i = 0; $i < 3; $i++ ){
    21 
    22     $pid = pcntl_fork();
    23     if( $pid == -1 ){
    24         exit('fork fail!' . PHP_EOL);
    25     }else if( $pid == 0 ){
    26 
    27         // 获得信号量
    28         sem_acquire($signal);
    29 
    30         //子进程从共享内存块中读取 写入值 +1 写回
    31         if ( shm_has_var($shm_id, SHARE_KEY) ){
    32             // 有值,加一
    33             $count = shm_get_var($shm_id, SHARE_KEY);
    34             $count ++;
    35             //模拟业务处理逻辑延迟
    36             $sec = rand( 1, 3 );
    37             sleep($sec);
    38 
    39             shm_put_var($shm_id, SHARE_KEY, $count);
    40         }else{
    41             // 无值,初始化
    42             $count = 0;
    43             //模拟业务处理逻辑延迟
    44             $sec = rand( 1, 3 );
    45             sleep($sec);
    46 
    47             shm_put_var($shm_id, SHARE_KEY, $count);
    48         }
    49 
    50         echo "child process " . getmypid() . " is writing ! now count is $count
    ";
    51         // 用完释放
    52         sem_release($signal);
    53         exit( "child process " . getmypid() . " end!
    " );
    54     }else{
    55         $childList[$pid] = 1;
    56     }
    57 }
    58 
    59 // 等待所有子进程结束
    60 while( !empty( $childList ) ){
    61     $childPid = pcntl_wait( $status );
    62     if ( $childPid > 0 ){
    63         unset( $childList[$childPid] );
    64     }
    65 }
    66 
    67 //父进程读取共享内存中的值
    68 $count = shm_get_var($shm_id, SHARE_KEY);
    69 echo "final count is " . $count . PHP_EOL;
    70 
    71 
    72 //3、去除内存共享区域
    73 #从系统中移除
    74 shm_remove($shm_id);
    75 #关闭和共享内存的连接
    76 shm_detach($shm_id);
    复制代码

      运行结果:

      完美的处理了进程之间抢资源的问题,实现了操作的原子性!

      参考文章:

      http://www.cnblogs.com/siqi/p/3999222.html

      http://www.cnblogs.com/siqi/p/3997444.html

      http://www.jianshu.com/p/08bcf724196b

  • 相关阅读:
    Unity进阶:行为树 01
    球球大作战 01 小球的移动和碰到金币,金币会消失。
    Fire Balls 03—— 多个圆环以及圆环的变速变向
    Unity经典案例之:Fire Balls 多个圆环以及圆环的变速变向
    Unity进阶之ET网络游戏开发框架 08-深入登录成功消息
    Unity进阶之ET网络游戏开发框架 07-修正游客登录的异步BUG
    Unity进阶之ET网络游戏开发框架 06-游客登录
    Unity进阶之ET网络游戏开发框架 05-搭建自己的第一个Scene
    Unity进阶之ET网络游戏开发框架 04-资源打包
    [转]深入理解闭包(二)
  • 原文地址:https://www.cnblogs.com/oxspirt/p/14777430.html
Copyright © 2020-2023  润新知