協程編程注意事項

      網友投稿 804 2022-05-29

      1.協程內部禁止使用全局變量,以免發生數據錯亂;(非多協程協作場景)

      原因:協程是共享進程資源的,也就是全局變量共享,用來處理任務時,全局變量很容易被別的協程篡改,導致數據錯亂。

      2.協程使用?use?關鍵字引入外部變量到當前作用域禁止使用引用,以免發生數據錯亂;

      (非多協程協作場景)

      原因:引用是原變量的真實地址,由于協程是共享進程資源的,會導致原變量很容易被別的協程篡改,導致數據錯亂。

      3.不能使用 ?(非多協程協作場景)

      (1)類靜態變量?Class::$array

      (2)全局變量?$_array

      (3)全局對象屬性?$object->array

      (4)其他超全局變量$GLOBALS ? 等保存協程上下文內容,以免發生數據錯亂;

      上下文Context類實際上采用標記協程id的方式來分發存儲各個協程對應的數據資源(數據池):

      use Swoole\Coroutine;

      class Context

      {

      協程編程注意事項

      protected static $pool = []; //進程創建后此靜態變量就會存在,但只會根據對應的id去覆蓋對應協程下的數據

      // 基于協程 `ID` 獲取數據

      static function get($key)

      {

      $cid = Coroutine::getCid();

      if ($cid < 0)

      {

      return null;

      }

      if(isset(self::$pool[$cid][$key])){

      return self::$pool[$cid][$key];

      }

      return null;

      }

      // 基于協程 `ID` 寫入數據

      static function put($key, $item)

      {

      $cid = Coroutine::getCid();

      if ($cid > 0)

      {

      self::$pool[$cid][$key] = $item;

      }

      }

      // 基于協程 `ID` 刪除數據

      static function delete($key = null)

      {

      $cid = Coroutine::getCid();

      if ($cid > 0)

      {

      if($key){

      unset(self::$pool[$cid][$key]);

      }else{

      unset(self::$pool[$cid]);

      }

      }

      }

      }

      4.協程之間通訊必須使用通道(Channel)場景:如果需要使用多協程協作執行任務時

      Coroutine\Channel?使用本地內存,不同的進程之間內存是隔離的。

      只能在同一進程的不同協程內進行?push?和?pop?操作。

      不過理論上仍然有共享內存的方式,只是需要進行上鎖,保持同步機制

      5.不能在多個協程間共用一個客戶端連接,以免發生數據錯亂;可以使用連接池實現;

      原因:同樣是因為連接標識共享,有可能前腳一個協程剛對鏈接做了操作,后腳被別的協程改了數據。(非多協程協作場景)

      $pool = new RedisPool();

      $server = new Swoole\Http\Server('127.0.0.1', 9501);

      $server->set([

      // 如開啟異步安全重啟, 需要在workerExit釋放連接池資源

      'reload_async' => true

      ]);

      $server->on('start', function (swoole_http_server $server) {

      var_dump($server->master_pid);

      });

      $server->on('workerExit', function (swoole_http_server $server) use ($pool) {

      $pool->destruct();

      });

      $server->on('request', function (swoole_http_request $req, swoole_http_response $resp) use ($pool) {

      //從連接池中獲取一個Redis協程客戶端

      $redis = $pool->get();

      //連接失敗

      if ($redis === false) {

      $resp->end("ERROR");

      return;

      }

      $result = $redis->hgetall('key');

      $resp->end(var_export($result, true));

      //釋放客戶端,其他協程可復用此對象

      $pool->put($redis);

      });

      $server->start();

      class RedisPool

      {

      protected $available = true;

      protected $pool;

      public function __construct()

      {

      $this->pool = new SplQueue;

      }

      public function put($redis)

      {

      $this->pool->push($redis);

      }

      /**

      * @return bool|mixed|\Swoole\Coroutine\Redis

      */

      public function get()

      {

      //有空閑連接且連接池處于可用狀態

      if ($this->available && count($this->pool) > 0) {

      return $this->pool->pop();

      }

      //無空閑連接,創建新連接

      $redis = new Swoole\Coroutine\Redis();

      $res = $redis->connect('127.0.0.1', 6379);

      if ($res == false) {

      return false;

      } else {

      return $redis;

      }

      }

      public function destruct()

      {

      // 連接池銷毀, 置不可用狀態, 防止新的客戶端進入常駐連接池, 導致服務器無法平滑退出

      $this->available = false;

      while (!$this->pool->isEmpty()) {

      $this->pool->pop();

      }

      }

      }

      6.在 Swoole\Server 中,客戶端連接應當在 onWorkerStart 中創建;

      原因:使得客戶端鏈接在整個進程周期中可用。

      7.在 Swoole\Process 中,客戶端連接應當在 Swoole\Process->start 后,子進程的回調函數中創建;

      原因:使得客戶端鏈接在整個子進程周期中可用。

      8.必須在協程內捕獲異常,不得跨協程捕獲異常;

      原因:多協程下,try/catch和throw在不同的協程中,協程內無法捕獲到此異常。當協程退出時,發現有未捕獲的異常,將引起致命錯誤。

      錯誤:

      try {

      Swoole\Coroutine::create(function () {

      throw new \RuntimeException(__FILE__, __LINE__);

      });

      }

      catch (\Throwable $e) {

      echo $e;

      }

      #try/catch和throw在不同的協程中,

      協程內無法捕獲到此異常。

      當協程退出時,發現有未捕獲的異常,將引起致命錯誤。

      正解:

      function test() {

      throw new \RuntimeException(__FILE__, __LINE__);

      }

      Swoole\Coroutine::create(function () {

      try {

      test();

      }

      catch (\Throwable $e) {

      echo $e;

      }

      });

      9.在__get /__set魔術方法中不能有協程切換。(跟php本身有關)

      任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Python:設計模式之單例模式
      下一篇:隨筆003-實戰HiLens最佳實踐中人臉檢測兩個小細節
      相關文章
      精品亚洲aⅴ在线观看| 亚洲国产香蕉碰碰人人| 亚洲网红精品大秀在线观看| 亚洲成a人片在线观看无码 | 亚洲精品国产高清嫩草影院| 亚洲AV色欲色欲WWW| 亚洲人成色4444在线观看| 亚洲综合色一区二区三区| 日韩亚洲产在线观看| 亚洲中文字幕人成乱码 | 亚洲成A∨人片在线观看无码| 青青草原精品国产亚洲av| 亚洲制服中文字幕第一区| 亚洲AV日韩AV鸥美在线观看| 亚洲成年人在线观看| 精品日韩亚洲AV无码一区二区三区| 色噜噜综合亚洲av中文无码| 91精品国产亚洲爽啪在线影院 | 一级毛片直播亚洲| 亚洲av高清在线观看一区二区 | 国产成人高清亚洲| 亚洲色偷偷偷鲁综合| 亚洲av永久无码精品古装片| 亚洲av无码专区在线播放| 亚洲欧洲免费视频| 亚洲欧洲日产韩国在线| 国产成人精品日本亚洲直接| 亚洲xxxxxx| 亚洲AV成人无码久久WWW| 日韩精品亚洲专区在线影视| 亚洲精品成人网久久久久久| 伊人亚洲综合青草青草久热| 国产aⅴ无码专区亚洲av| 亚洲午夜未满十八勿入| 亚洲不卡视频在线观看| 亚洲国产成人AV在线播放| 亚洲精品乱码久久久久久不卡| 国产成人亚洲精品狼色在线| 水蜜桃亚洲一二三四在线| 91亚洲视频在线观看| 亚洲国产一区二区三区在线观看|