美文网首页
基于php的pcntl_alarm()写的同步定时器

基于php的pcntl_alarm()写的同步定时器

作者: 金星show | 来源:发表于2019-03-14 10:12 被阅读0次
    <?php
    header("Content-Type:text/html;charset=utf8");
    set_time_limit(0);
    //ini_set('max_execution_time',0);
    /**
     * 定时器
     * 监听alarm信号,并通过alarm信号来控制每次的执行.即收到alarm信号后开始执行定时器任务
     * 比如每十秒发送一次Alarm信号,那么每十秒就执行一次定时器任务
     * 如有事件支持时,比如Select或者Libevent等,则不再使用alarm信号来辅助
     */
    class Timer
    {
        //事件机制.为空时则使用alarm信号来完成定时器机制.不为空时依赖Select/Libevent等
        private static $_event = null;
        //任务列表
        private static $_taskList = array();
        //任务ID
        private static $_id = 1;
    
        /**
         * 初始化定时器
         * @param object $event
         */
        public static function init($event = null)
        {
            //如果没有事件,则安装一个alarm信号处理器
            echo "初始化成功,安装一个alarm信号处理器".PHP_EOL;
            pcntl_signal(SIGALRM, array('Timer', 'signalCallback'));
            //is_object($event) ? (self::$_event = $event) : (pcntl_signal(SIGALRM, array('Timer', 'signalCallback'), false));
        }
    
        /**
         * 添加任务
         * @param $callback string|array 回调函数
         * @param $args array 参数
         * @param $intervalSecond int 每次执行的间隔时间,单位秒,必须大于0
         * @param $isAlways bool|true 是否一直执行,默认为true. 一次性任务请传入false
         * @return int|false
         */
        public static function add($callback, array $args, $intervalSecond, $isAlways = true,$old_timer_id = '')
        {
            if ($intervalSecond <= 0 || !is_callable($callback)) {
                return false;
            }
            if (!is_null(self::$_event)) {
                //return self::$_event->add($callback, $args, $intervalSecond, $isAlways ? EventInterface::EVENT_TYPE_TIMER : EventInterface::EVENT_TYPE_TIMER_ONCE);
            } else {
                //echo '添加成功'.PHP_EOL;
                pcntl_alarm(1);
                $startTime = time() + $intervalSecond;
                $timerId = self::$_id++;
    
                if($old_timer_id){
                    $timerId = $old_timer_id;
                }
                self::$_taskList[$timerId] = array($callback, $args, $startTime, $intervalSecond, $isAlways,$timerId);
                return $timerId;
            }
        }
    
        /**
         * 删除一个定时器
         * @param $timerId
         */
        public static function delOne($timerId)
        {
            if (!is_null(self::$_event)) {
                //self::$_event->delOne($timerId, EventInterface::EVENT_TYPE_TIMER);
            } else {
                unset(self::$_taskList[$timerId]);
            }
        }
    
        /**
         * 删除所有的定时器任务
         */
        public static function delAll()
        {
            self::$_taskList = array();
            pcntl_alarm(0);
            if (!is_null(self::$_event)) {
                //self::$_event->delAllTimer();
            }
        }
    
        /**
         * 信号处理函数
         */
        public static  function signalCallback()
        {
            //没有事件机制,并且队列不为空,则使用alarm信号
            if (is_null(self::$_event) && !empty(self::$_taskList)) {
                echo "定时器收到信号".PHP_EOL;
                //创建一个计时器,每秒向进程发送一个alarm信号。
                pcntl_alarm(1);
                self::_execute();
            }
        }
    
        /**
         * 执行任务
         */
        private static function _execute()
        {
            $nowTime = time();
            echo "开始执行".PHP_EOL;
            foreach (self::$_taskList as $timerId => $task) {
                //当前时间小于启动时间,则不启动该时间段任务
                if ($nowTime < $task[2]) {
                    continue;
                }
                //如果是持续性定时器任务,则添加到下次执行的队伍中
                if ($task[4]) {
                    self::add($task[0], $task[1], $task[3], $task[4],$task[5]);
                }
                //执行回调函数
                try {
                    echo '任务id:'.$timerId.PHP_EOL;
                    call_user_func_array($task[0], $task[1]);
                } catch (\Exception $e) {
                    Log::write('execution callback function timer execute-' . $task[0] . ' throw exception' . json_encode($e), 'ERROR');
                }
                //删除本次已经执行的任务
                //unset(self::$_taskList[$timerId]);
            }
        }
    }
    
    
    
    Timer::init();
    $timer_id1 = Timer::add('test',array('我是任务一'),5,true); //添加定时任务  5秒执行一次,某个方法。
    $timer_id2 = Timer::add('test',array('我是任务二'),5,true);
    
    Timer::delOne($timer_id1);
    Timer::delOne($timer_id2);
    
    try{
        while(true){
            pcntl_signal_dispatch();
    
            sleep(10);//模拟进程执行任务 挂起
            pcntl_signal_dispatch();
        }
    }catch(\Exception $e){
        echo $e->getMessage();
    }
    
    
    function test($msg){
        echo $msg.'-'.time().PHP_EOL;
    }
    
    
    //接受任务 参数处理 定时信号  信号接收处理  抛出信号
    

    相关文章

      网友评论

          本文标题:基于php的pcntl_alarm()写的同步定时器

          本文链接:https://www.haomeiwen.com/subject/mnofmqtx.html