Mix PHP V2 新特性:協程、定時器

onanying發表於2019-04-13

協程

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 <coder.keda@gmail.com>
 */
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 <coder.keda@gmail.com>
 */
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 <coder.keda@gmail.com>
 */
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();
    }

}
複製程式碼

相關文章