最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • PHP进程间通信

    正文概述 转载于:掘金(ethreads)   2020-12-17   256

    php是用C编写的,因此它对系统底层API的操作与C很像,同大多数语言一样,PHP进程间通信的方式有以下几种:消息队列,管道,共享内存,socket和信号。本文是对这几种通信方式对整理:

    管道通信PIPE

    管道用于承载简称之间的通讯数据。为了方便理解,可以将管道比作文件,进程A将数据写到管道P中,然后进程B从管道P中读取数据。php提供的管道操作API与操作文件的API基本一样,除了创建管道使用posix_mkfifo函数,读写等操作均与文件操作函数相同。当然,你可以直接使用文件模拟管道,但是那样无法使用管道的特性了。

    通过管道通信的大概思路是,首先创建一个管道,然后子进程向管道中写入信息,父进程从管道中读取信息,这样就可以做到父子进程直接实现通信了。

    <?php
    
    // 创建管道
    $pipePath = "pipe";
    if( !file_exists( $pipePath ) ){
        if( !posix_mkfifo( $pipePath, 0666) ){
            exit('make pipe false!' . PHP_EOL);
        }
    }
    
    // 创建进程,子进程写管道,父进程读管道
    // 通过 pcntl_fork函数创建一个子进程。
    // pcntl_fork 函数 很特殊,它调用一次拥有 多个返回值。
    // 在父进程中:它返回 子进程的ID 这个值是 大于0 的。
    // 在子进程中,它返回0。当返回 -1 时表示创建进程失败。
    $pid = pcntl_fork();
    
    if( $pid == 0 ){
        // 子进程写管道
        $file = fopen( $pipePath, 'w');
        fwrite( $file, 'hello world');
        sleep(1);
        exit;
    }else{
        // 父进程读管道
        $file = fopen( $pipePath, 'r');
        // 设置成读取非阻塞
        // 当读取是非阻塞的情况下,父进程进行读取信息的时候不会等待,
        // 管道中没有消息也会立马返回。
        // stream_set_blocking( $file, False); 
        echo fread( $file, 20) . PHP_EOL;
    
        pcntl_wait($status); // 回收子进程
    }
    

    消息队列

    消息队列是存放在内存中的一种队列数据结构。

    <?php
    
    // 获取父进程id
    $parentPid = posix_getpid();
    echo "parent progress pid:{$parentPid}\n";
    $childList = array();
    // 创建消息队列,定义消息类型
    $id = ftok(__FILE__, 'm');
    $msgQueue = msg_get_queue($id);
    const MSG_TYEP = 1;
    
    // 生产者
    function producer()
    {
        global $msgQueue;
        $pid = posix_getpid();
        $repeatNum = 5;
        for ($i = 0; $i <= $repeatNum; $i++) {
            $str = "({$pid}) progress create! {$i}";
            msg_send($msgQueue, MSG_TYEP, $str);
            $rand = rand(1, 3);
            sleep($rand);
        }
    }
    
    // 消费者
    function consumer()
    {
        global $msgQueue;
        $pid = posix_getpid();
        $repeatNum = 6;
        for ($i = 1; $i<= $repeatNum; $i++) {
            $rel = msg_receive($msgQueue, MSG_TYEP, $msgType, 1024, $message);
            echo "{$message} | consumer({$pid}) destroy \n";
            $rand = rand(1, 3);
            sleep($rand);
        }
    }
    
    function createProgress($callback)
    {
        $pid = pcntl_fork();
        if ($pid == -1) {
            // 创建失败
            exit("fork progresses error\n");
        } elseif ($pid == 0) {
            // 子进程执行程序
            $pid = posix_getpid();
            $callback();
            exit("({$pid})child progress end!\n");
        } else {
            // 父进程
            return $pid;
        }
    }
    
    for ($i = 0; $i < 3; $i++) {
        $pid = createProgress('producer');
        $childList[$pid] = 1;
        echo "create producer progresses: {$pid}\n";
    }
    
    for ($i = 0; $i < 2; $i++) {
        $pid = createProgress('consumer');
        $childList[$pid] = 1;
        echo "create consumer progresses: {$pid}\n";
    }
    
    while (!empty($childList)) {
        $childPid = pcntl_wait($status);
        if ($childPid > 0) {
            unset($childList[$childPid]);
        }
    }
    echo "({$parentPid})main progress end!\n";
    

    运行结果:

    create producer progresses: 21432
    create producer progresses: 21433
    create producer progresses: 21434
    create consumer progresses: 21435
    (21426) progress create! 2 | consumer(21435) destroy
    (21424) progress create! 1 | consumer(21436) destroy
    create consumer progresses: 21436
    (21426) progress create! 3 | consumer(21436) destroy
    (21426) progress create! 4 | consumer(21435) destroy
    (21425) progress create! 3 | consumer(21436) destroy
    (21424) progress create! 2 | consumer(21435) destroy
    (21426) progress create! 5 | consumer(21435) destroy
    (21424) progress create! 3 | consumer(21436) destroy
    (21433)child progress end!
    (21425) progress create! 4 | consumer(21435) destroy
    (21424) progress create! 4 | consumer(21436) destroy
    (21434)child progress end!
    (21424) progress create! 5 | consumer(21435) destroy
    (21425) progress create! 5 | consumer(21436) destroy
    (21432)child progress end!
    (21435)child progress end!
    (21436)child progress end!
    (21431)main progress end!
    

    信号量与共享内存

    <?php
    $parentPid = posix_getpid();
    echo "parent progress pid:{$parentPid}\n";
    
    // 创建共享内存,创建信号量,定义共享key
    // ftok(文件路径,资源标识符) 创建一个IPC通信所需的id
    $shm_id = ftok(__FILE__, 'm');
    $shm_id = ftok(__FILE__, 's');
    // shm_attach(id) 创建或者打开一个共享内存
    $shareMemory = shm_attach($shm_id);
    // 返回一个可用户访问系统信号量的id
    $signal = sem_get($shm_id);
    const SHARE_KEY = 1;
    // 生产者
    function producer() {
        global $shareMemory;
        global $signal;
        $pid = posix_getpid();
        $repeatNum = 5;
        for ($i = 1; $i <= $repeatNum; $i++) {
            // 获得信号量 - 阻塞进程,直到信号量被获取到[lock锁机制的关键]
            sem_acquire($signal);
    
            // 检查某个key是否存在与共享内存中
            if (shm_has_var($shareMemory, SHARE_KEY)) {
                // 获取共享内存中的key的值
                $count = shm_get_var($shareMemory, SHARE_KEY);
                $count ++;
                // 为共享内存中的key赋值
                shm_put_var($shareMemory, SHARE_KEY, $count);
                echo "({$pid}) count: {$count}\n";
            } else {
                // 初始化
                shm_put_var($shareMemory, SHARE_KEY, 0);
                echo "({$pid}) count: 0\n";
            }
            // 释放
            sem_release($signal);
        }
    }
    
    function createProgress($callback) {
        $pid = pcntl_fork();
        if ($pid == -1) {
            // 创建失败
            exit("fork progress error!\n");
        } elseif ($pid == 0) {
            // 子进程
            $pid = posix_getpid();
            $callback();
            exit("({$pid}) child progress end!\n");
        } else {
            // 父进程
            return $pid;
        }
    }
    
    // 3个写进程
    for ($i = 0; $i < 3; $i ++) {
        $pid = createProgress('producer');
        $childList[$pid] = 1;
        echo "create producer child progress: {$pid} \n";
    }
    
    // 等待所有子进程
    while (!empty($childList)) {
        $childPid = pcntl_wait($status);
        if ($childPid > 0) {
            unset($childList[$childPid]);
        }
    }
    
    // 释放共享内存与信号量
    shm_remove($shareMemory);
    sem_remove($signal);
    echo "({$parentPid}) main progress end!\n";
    
    

    运行结果: 使用信号量来实现共享内存的锁机制

    parent progress pid:31720
    create producer child progress: 31721 
    create producer child progress: 31722 
    (31721) count: 0
    (31721) count: 1
    (31721) count: 2
    (31721) count: 3
    (31721) count: 4
    (31721) child progress end!
    create producer child progress: 31723 
    (31722) count: 5
    (31722) count: 6
    (31722) count: 7
    (31722) count: 8
    (31722) count: 9
    (31722) child progress end!
    (31723) count: 10
    (31723) count: 11
    (31723) count: 12
    (31723) count: 13
    (31723) count: 14
    (31723) child progress end!
    (31720) main progress end!
    

    无锁情况

    Warning: sem_release(): SysV semaphore 4357894312 (key 0x73048925) is not currently acquired in /Users/easyboom/www/example/信号量与共享内存.php on line 38
    

    起源地 » PHP进程间通信

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元