<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          PHP 多任務(wù)秒級(jí)定時(shí)器的實(shí)現(xiàn)方法

          共 6710字,需瀏覽 14分鐘

           ·

          2022-07-26 15:58

          1.描述

          最近在公司部署crontab的時(shí)候,突發(fā)奇想是否可以用PHP去實(shí)現(xiàn)一個(gè)定時(shí)器,顆粒度到秒級(jí)就好,因?yàn)閏rontab最多到分鐘級(jí)別,同時(shí)也調(diào)研了一下用PHP去實(shí)現(xiàn)的定時(shí)器還真不太多,Swoole 擴(kuò)展里面到實(shí)現(xiàn)了一個(gè)毫秒級(jí)的定時(shí)器很高效,但畢竟不是純PHP代碼寫的,所以最后還是考慮用PHP去實(shí)現(xiàn)一個(gè)定時(shí)器類,以供學(xué)習(xí)參考。

          2.代碼實(shí)現(xiàn)

          在實(shí)現(xiàn)定時(shí)器代碼的時(shí)候,用到了PHP系統(tǒng)自帶的兩個(gè)擴(kuò)展

          Pcntl - 多進(jìn)程擴(kuò)展 :

          主要就是讓PHP可以同時(shí)開(kāi)啟很多子進(jìn)程,并行的去處理一些任務(wù)。

          Spl - SplMinHeap - 小頂堆

          一個(gè)小頂堆數(shù)據(jù)結(jié)構(gòu),在實(shí)現(xiàn)定時(shí)器的時(shí)候,采用這種結(jié)構(gòu)效率還是不錯(cuò)的,插入、刪除的時(shí)間復(fù)雜度都是 O(logN) ,像 libevent 的定時(shí)器也在 1.4 版本以后采用了這種數(shù)據(jù)結(jié)構(gòu)之前用的是 rbtree,如果要是使用鏈表或者固定的數(shù)組,每次插入、刪除可能都需要重新遍歷或者排序,還是有一定的性能問(wèn)題的。

          3.流程


          說(shuō)明

          1、定義定時(shí)器結(jié)構(gòu),有什么參數(shù)之類的.
          2、然后全部注冊(cè)進(jìn)我們的定時(shí)器類 Timer.
           3、調(diào)用定時(shí)器類的monitor方法,開(kāi)始進(jìn)行監(jiān)聽(tīng).
          4、監(jiān)聽(tīng)過(guò)程就是一個(gè)while死循環(huán),不斷的去看時(shí)間堆的堆頂是否到期了,本來(lái)考慮每秒循環(huán)看一次,后來(lái)一想每秒循環(huán)看一次還是有點(diǎn)問(wèn)題,如果正好在我們sleep(1)的時(shí)候定時(shí)器有到期的了,那我們就不能馬上去精準(zhǔn)執(zhí)行,可能會(huì)有延時(shí)的風(fēng)險(xiǎn),所以還是采用 usleep(1000) 毫秒級(jí)的去看并且也可以將進(jìn)程掛起減輕 CPU 負(fù)載.

          4.代碼

          <?php

          /**** Class Timer*/
          class Timer extends SplMinHeap
          {
              /**
               * 比較根節(jié)點(diǎn)和新插入節(jié)點(diǎn)大小  
               * @param mixed $value1 
               * @param mixed $value2  
               * @return int 
               */

              protected function compare($value1, $value2)
              
          {
                  if ($value1['timeout'] > $value2['timeout']) {
                      return -1;
                  }
                  if ($value1['timeout'] < $value2['timeout']) {
                      return 1;
                  }
                  return 0;
              }

              /**  
               * 插入節(jié)點(diǎn)  
               * @param mixed $value 
               */

              public function insert($value)
              
          {
                  $value['timeout'] = time() + $value['expire'];
                  parent::insert($value);
              }

              /**
               * 監(jiān)聽(tīng)  
               * @param bool $debug 
               */

              public function monitor($debug = false)
              
          {
                  while (!$this->isEmpty()) {
                      $this->exec($debug);
                      usleep(1000);
                  }
              }

              /**  
               * 執(zhí)行  
               * @param $debug 
               */

              private function exec($debug)
              
          {
                  $hit = 0;
                  $t1 = microtime(true);
                  while (!$this->isEmpty()) {
                      $node = $this->top();
                      if ($node['timeout'] <= time()) {        //出堆或入堆
                          $node['repeat'] ? $this->insert($this->extract()) : $this->extract();
                          $hit = 1;        //開(kāi)啟子進(jìn)程
                          if (pcntl_fork() == 0) {
                              empty($node['action']) ? '' : call_user_func($node['action']);
                              exit(0);
                          }
                          //忽略子進(jìn)程,子進(jìn)程退出由系統(tǒng)回收
                          pcntl_signal(SIGCLD, SIG_IGN);
                      } else {
                          break;
                      }
                  }
                  $t2 = microtime(true);
                  echo ($debug && $hit) ? '時(shí)間堆 - 調(diào)整耗時(shí): ' . round($t2 - $t1, 3) . "秒\r\n" : '';
              }
          }

          5.實(shí)例

          <?php
          $timer = new Timer();
          //注冊(cè) - 3s - 重復(fù)觸發(fā)
          $timer->insert(array('expire' => 3'repeat' => true'action' => function () {
              echo '3秒 - 重復(fù) - hello world' . "\r\n";
          }));

          //注冊(cè) - 3s - 重復(fù)觸發(fā)
          $timer->insert(array('expire' => 3'repeat' => true'action' => function () {
              echo '3秒 - 重復(fù) - gogo' . "\r\n";
          }));

          //注冊(cè) - 6s - 觸發(fā)一次
          $timer->insert(array('expire' => 6'repeat' => false'action' => function () {
              echo '6秒 - 一次 - hello xxxx' . "\r\n";
          }));

          //監(jiān)聽(tīng)
          $timer->monitor(false)

          6.執(zhí)行結(jié)果


          也測(cè)試過(guò)比較極端的情況,同時(shí)1000個(gè)定時(shí)器1s全部到期,時(shí)間堆全部調(diào)整完僅需 0.126s 這是沒(méi)問(wèn)題的,但是每調(diào)整完一個(gè)定時(shí)器就需要去開(kāi)啟一個(gè)子進(jìn)程,這塊可能比較耗時(shí)了,有可能1s處理不完這1000個(gè),就會(huì)影響下次監(jiān)聽(tīng)繼續(xù)觸發(fā),但是不開(kāi)啟子進(jìn)程,比如直接執(zhí)行應(yīng)該還是可以處理完的。。。。當(dāng)然肯定有更好的方法,目前只能想到這樣。

          相關(guān)文章

          Linux Crontab 定時(shí)任務(wù)詳解

          php Swoole實(shí)現(xiàn)毫秒級(jí)定時(shí)任務(wù)

          淺析如何使用好swoole毫秒級(jí)定時(shí)器功能?

          linux定時(shí)刪除N天前的文件(文件夾)

          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情五月婷 | 久久无码高清视频 | 韩国三级片久久久久 | 无码日本精品久久久久久蜜桃 | 日本一区二区三区四区五区六区 |