首先翻到官網https://wiki.swoole.com/#/coroutine/channel。
有關channel:通道,用於協程間通訊,支持多生產者協程和多消費者協程。底層自動實現了協程的切換和調度。
其構造方法:Swoole\Coroutine\Channel->__construct(int $capacity = 1),有個capacity的容量參數,一開始並不理解, 敲點代碼嘗試下,理解起來容易許多。
[root@guangzhou coroutine]# cat coroutine_channel.php <?php #開啟一鍵協程 Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); Co\run(function(){ // 設置一個容量為1的通道 $chan = new Swoole\Coroutine\Channel(1); Swoole\Coroutine::create(function () use ($chan) { for($i = 0; $i < 6; $i++) { $chan->push( date('H:i:s') . " 數據 i:" . $i . "\n"); echo date('H:i:s') . " 第{$i}次塞數據時間\n"; } }); Swoole\Coroutine::create(function () use ($chan) { while(1) { $data = $chan->pop(); if($data){ echo date('H:i:s') . " 取數據時間\n"; echo $data . "\n"; Co::sleep(2); }else{ break; } } }); });
//看到這段代碼,你認為他的輸出情況是怎樣,先自己思考下!!
上面腳本運行后每次pop后獲取通道數據后休眠2秒,是協程模式,可能也會想當然認為每次取到數據的間隔應該是2秒??
現在運行腳本:
[root@guangzhou coroutine]# php coroutine_channel.php 06:55:32 第0次塞數據時間 06:55:32 第1次塞數據時間 06:55:32 取數據時間 06:55:32 數據 i:0 06:55:34 第2次塞數據時間 06:55:34 取數據時間 06:55:32 數據 i:1 06:55:36 第3次塞數據時間 06:55:36 取數據時間 06:55:32 數據 i:2 06:55:38 第4次塞數據時間 06:55:38 取數據時間 06:55:34 數據 i:3 06:55:40 第5次塞數據時間 06:55:40 取數據時間 06:55:36 數據 i:4 06:55:42 取數據時間 06:55:38 數據 i:5
有沒有發現前三次取數據時間點都是2秒的,第四次的數據一下子到了4秒。
經過swoole官方群的高人指點,給出了正確的執行路徑如下圖(數字從小到大是執行順序):
因為設置的capacity=1,導致第二次的push未成功,需要先pop方能繼續下去。
第一次pop后,回到第三次push循環,程序判斷管道已滿這時將處理第一次pop的數據,又開始第四次push循環,程序判斷管道又滿了,pop第二次push的數據。
到目前為止前三次push的數據並未進入到sleep這里,導致后續打印第一次pop數據時,塞數據時間點比取數據時間點被推后了。
從第三次數據pop后的數據都要經歷push失敗->echo+sleep->pop上一次的,這里就會有休眠2秒的時間加上pop上次時間就是4秒了。
這里有點繞,需要多思考並動手實踐。
修改capacity=10,並push循環7次,運行程序:
[root@guangzhou coroutine]# cat coroutine_channel.php <?php #開啟一鍵協程 Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); Co\run(function(){ // 設置一個容量為1的通道 $chan = new Swoole\Coroutine\Channel(10); Swoole\Coroutine::create(function () use ($chan) { for($i = 0; $i < 7; $i++) { $chan->push( date('H:i:s') . " 數據 i:" . $i . "\n"); echo date('H:i:s') . " 第{$i}次塞數據時間\n"; } }); Swoole\Coroutine::create(function () use ($chan) { while(1) { $data = $chan->pop(); if($data){ echo date('H:i:s') . " 取數據時間\n"; echo $data . "\n"; Co::sleep(2); }else{ break; } } }); });
執行腳本:
[root@guangzhou coroutine]# php coroutine_channel.php 07:29:22 第0次塞數據時間 07:29:22 第1次塞數據時間 07:29:22 第2次塞數據時間 07:29:22 第3次塞數據時間 07:29:22 第4次塞數據時間 07:29:22 第5次塞數據時間 07:29:22 第6次塞數據時間 07:29:22 取數據時間 07:29:22 數據 i:0 07:29:24 取數據時間 07:29:22 數據 i:1 07:29:26 取數據時間 07:29:22 數據 i:2 07:29:28 取數據時間 07:29:22 數據 i:3 07:29:30 取數據時間 07:29:22 數據 i:4 07:29:32 取數據時間 07:29:22 數據 i:5 07:29:34 取數據時間 07:29:22 數據 i:6
發現所有 i:xxx 的時間點幾乎一致。
結論即是capacity的作用是限制管道寫入的長度,如果超出寫入長度限制則會寫入失敗,等數據pop后數據量少於capacity等數值才能push管道寫入成功。