• PHP多进程环境下通过共享内存与信号量实现资源共享


    PHP多进程环境下通过共享内存与信号量实现资源共享

    目前工作环境,由于一些原因,不能使用swoole,和其他多进程的管理组件。但是项目中有大量的功能必须通过多进程来实现。面对这也不能,那也不能的困境,总要想一些办法来多快好省的完成工作。

    项目中其他成员,使用多进程的方式,通过shell 起多个脚本,完成多进程的开发,效果也不错。我这边主要使用pcntl_fork 这个PHP自带的扩展来起进程。

    在项目的开发过程中,比如我需要通过10个进程消费100万的数据,在进程环境内,无法将多个进程已经消费的数据汇总起来。

    需求点:

    多进程环境下,进程之间通信困难,编写通信代码也比较繁琐。
    进程间通信,比如获取值,对值进行操作,面临锁的问题。

    下面是通过共享内存,与信号量实现的进程间通信

    <?php
    
    class Test
    {
        private $processArr = [];
        private $shmId = null;
        private $semId = null;
        private $ftokId = null;
    
        const SHARE_MEMORY_SIZE = 4096;  //申请4k 作为共享内存
    
        //共享内存配置类似
        private $shareKeyConfig = array(
            "count"   => array(
                "start" => 0,
                "end"   => 127,
            ),
            "consume" => array(
                "start" => 128,
                "end"   => 255,
            ),
        );
    
        /**
         * 初始化参数
         * Test constructor.
         * @param array $shareKeyConfig
         */
        public function __construct($shareKeyConfig = [])
        {
            if ($shareKeyConfig) {
                $this->shareKeyConfig = $shareKeyConfig;
            }
            return true;
        }
    
    
        public function renderProgress($current)
        {
            printf("progress: [%-50s] %d%% Done
    ", str_repeat('#', $current / 600 * 50), $current / 600 * 100);
        }
    
    
    
        /**
         * 启动一个进程
         */
        public function run()
        {
            $this->initShareMemoryLock();
            for ($i = 0; $i <= 5; $i++) {
                $pid = pcntl_fork();
                if ($pid == -1) {
                    die("创建子进程失败");
                } elseif ($pid > 0) {
                    //echo "子进程{$pid}已经正常启动" . PHP_EOL;
                    $this->processArr[$pid] = $pid;
                } else {
                    $this->dosomeThing();
                    exit();
                }
            }
            //echo "done" . PHP_EOL;
            $this->waitProcess();
            //echo "done2" . PHP_EOL;
            $this->removeShareMemoryLock();
        }
    
    
        public function dosomeThing()
        {
            for ($i = 1; $i <= 100; $i++) {
                usleep(100000);
                $this->incr("consume");
                $num = (int)$this->getValue('consume');
                $this->renderProgress($num);
                //echo "当前内存中的数字是:{$num}" . PHP_EOL;
            }
            //sleep(1);
            //echo "加完毕" . PHP_EOL;
            return true;
        }
    
    
        public function waitProcess()
        {
            while (count($this->processArr)) {
                $childPid = pcntl_wait($status);
                //var_dump($childPid);
                if ($childPid > 0) {
                    //echo "子进程{$childPid}已经正常结束" . PHP_EOL;
                    unset($this->processArr[$childPid]);
                }
            }
            return true;
        }
    
        /**
         * 初始化基于共享内存的锁
         */
        public function initShareMemoryLock()
        {
            $this->checkFunctionExists();
            $this->createShareMemoryCache(1);
            $this->createSemaphore(1);
            return true;
        }
    
        /**
         * 计数器
         * @param $key
         * @param int $incr
         */
        public function incr($key, $incr = 1)
        {
            sem_acquire($this->semId);
            $num = (int)$this->getValue($key);
            $this->setValue($key, $num + $incr);
            sem_release($this->semId);
            return true;
        }
    
        /**
         * 根据key,获取对应的value
         * @param string $key
         */
        private function getValue(string $key)
        {
            if (!isset($this->shareKeyConfig[$key])) {
                die('请先配置共享内存的key!' . PHP_EOL);
            }
            $config = $this->shareKeyConfig[$key];
            $val    = shmop_read($this->shmId, $config['start'], ($config['end'] - $config['start']));
            return trim($val);
        }
    
        /**
         * 设置共享内存
         * @param string $key
         * @param string $val
         */
        private function setValue(string $key, string $val)
        {
            if (!isset($this->shareKeyConfig[$key])) {
                die('请先配置共享内存的key!' . PHP_EOL);
            }
            $config = $this->shareKeyConfig[$key];
            shmop_write($this->shmId, $val, $config['start']);
            return true;
        }
    
        /**
         * 创建共享内存
         * 单个文件多个方法调用,必须使用不同的projectId
         * @param $projectId
         * @param int $size
         * @return bool
         */
        public function createShareMemoryCache($projectId, $size = 2048)
        {
            set_time_limit(0);
            if ($projectId < 1 || $projectId > 255) {
                die("projectId 的取值范围在1-255。" . PHP_EOL);
            }
    
            $this->ftokId = ftok(__FILE__, $projectId);
            $shmId        = @shmop_open($this->ftokId, "c", 0644, $size);
            if (!is_resource($shmId)) {
                die("shmop_open(): unable to attach or create shared memory segment 'Permission denied'" . PHP_EOL);
            }
            $this->shmId = $shmId;
            return true;
        }
    
        /**
         * 关闭共享内存快
         */
        public function closeShareMemory()
        {
            shmop_delete($this->shmId);
            shmop_close($this->shmId);
            return true;
        }
    
        /**
         * 关闭信号量
         */
        public function closeSemaphore()
        {
            sem_remove($this->semId);
            return true;
        }
    
    
        /**
         * 关闭共享内存
         * @return bool
         */
        public function removeShareMemoryLock()
        {
            $this->closeSemaphore();
            $this->closeShareMemory();
            return true;
        }
    
    
        /**
         *  创建信号量
         * @param $projectId
         */
        public function createSemaphore($projectId)
        {
            $this->semId = sem_get($this->ftokId);
            return true;
        }
    
        /**
         * 检测系统是否开启共享内存与信号量函数
         * @return bool
         */
        public function checkFunctionExists()
        {
            $requireFunc = array(
                "ftok",
                "shmop_open",
                "shmop_write",
                "shmop_read",
                "shmop_delete",
                "shmop_close",
                "shmop_size",
                "sem_get",
                "sem_acquire",
                "sem_release",
                "sem_remove"
            );
    
            foreach ($requireFunc as $func) {
                if (!function_exists($func)) {
                    die("$func 删除不存在");
                }
            }
            return true;
        }
    }
    
    $demo = new Test();
    $demo->run();
    
    
  • 相关阅读:
    新生题目集
    C语言课程设计
    Java学习-8
    Java学习-7
    Java学习-6
    Java学习-5
    HTML学习
    Java学习-4
    强大的项目管理工具maven
    java学习-3
  • 原文地址:https://www.cnblogs.com/roverliang/p/12674505.html
Copyright © 2020-2023  润新知