Mix PHP V2 新特性:協程、定時器
協程
Mix PHP V2 基於 Swoole 4 的 PHP Stream Hook 協程技術開發,協程使用方式與 Golang 幾乎一致,包括框架封裝的協程池、連線池、命令列處理都大量參考了 Golang 的系統庫風格。
除了缺少 select case 外,Mix PHP 與 Golang 的協程幾乎一致,框架還提供了連線池、協程池、命令列處理這些開箱即用的封裝。
xgo + Channel
xgo 類似 Golang 的 go 關鍵字,可啟動一個新的協程,Channel 等於 Golang 的 chan 類,負責在不同協程中傳遞資料。
<?php namespace Console\Commands; use Mix\Core\Coroutine\Channel; use Mix\Core\Event; /** * Class CoroutineCommand * @package Console\Commands * @author liu,jian <[email protected]> */ class CoroutineCommand { /** * 主函式 */ public function main() { xgo(function () { $time = time(); $chan = new Channel(); for ($i = 0; $i < 2; $i++) { xgo([$this, 'foo'], $chan); } for ($i = 0; $i < 2; $i++) { $result = $chan->pop(); } println('Total time: ' . (time() - $time)); }); Event::wait(); } /** * 查詢資料 * @param Channel $chan */ public function foo(Channel $chan) { $db= app()->dbPool->getConnection(); $result = $db->createCommand('select sleep(5)')->queryAll(); $db->release(); // 不手動釋放的連線不會歸還連線池,會在析構時丟棄 $chan->push($result); } }
執行結果為 5s,說明是並行執行的。
WaitGroup + xdefer
WaitGroup 與 Golang 的完全一致,xdefer 方法也等同於 Golang 的 defer 關鍵字。
當並行執行且不需要返回結果時,可以使用 WaitGroup + xdefer,xdefer 即使在方法丟擲異常時,仍然會執行,這樣能避免一直處於阻塞狀態。
<?php namespace Console\Commands; use Mix\Concurrent\Sync\WaitGroup; use Mix\Core\Event; /** * Class WaitGroupCommand * @package Console\Commands * @author liu,jian <[email protected]> */ class WaitGroupCommand { /** * 主函式 */ public function main() { xgo(function () { $wg = WaitGroup::new(); for ($i = 0; $i < 2; $i++) { $wg->add(1); xgo([$this, 'foo'], $wg); } $wg->wait(); println('All done!'); }); Event::wait(); } /** * 查詢資料 * @param WaitGroup $wg */ public function foo(WaitGroup $wg) { xdefer(function () use ($wg) { $wg->done(); }); println('work'); throw new \RuntimeException('ERROR'); } }
即便丟擲了 RuntimeException 異常,仍然能執行到println('All done!');
,沒有導致 wg 內的 chan 一直處於阻塞狀態。
定時器
非同步程式設計中,定時器的使用非常頻繁。
Timer::new() after tick $timer->clear();
<?php namespace Console\Commands; use Mix\Core\Event; use Mix\Core\Timer; /** * Class TimerCommand * @package Console\Commands * @author liu,jian <[email protected]> */ class TimerCommand { /** * 主函式 */ public function main() { // 一次性定時 Timer::new()->after(1000, function () { println(time()); }); // 持續定時 $timer = new Timer(); $timer->tick(1000, function () { println(time()); }); // 停止定時 Timer::new()->after(10000, function () use ($timer) { $timer->clear(); }); Event::wait(); } }