コルーチン

コルーチンは、スレッドよりも軽量なユーザーレベルの同時実行メカニズムであり、プロセス内でのマルチタスクスケジューリングを実現します。コルーチン間の切り替えを手動で停止および再開することで、プロセスのコンテキストスイッチのオーバーヘッドを回避します。
workermanは、Swoole/Swow/Fiberドライバと自動互換性のある汎用コルーチンインターフェースを提供します。

ヒント
この機能は workerman>=5.1.0 が必要です

注意

  • コルーチンはSwoole Swow Fiberドライバのみをサポートしています
  • Fiberドライバを使用する場合は、composer require revolt/event-loopをインストールする必要があります
  • SwooleまたはSwowドライバは、PHPのブロッキング関数を自動的にコルーチン化することができ、元の同期コードを非同期で実行します
  • しかし、FiberSwooleSwowのように自動コルーチン化はできず、PHPに組み込まれたブロッキング関数に遭遇すると、プロセス全体がブロックされ、コルーチン切り替えは発生しません
  • Swoole Swow Fiberドライバを使用する場合、workermanは毎回onWorkerStart onMessage onConnect onCloseなどのコールバックを実行する際に自動的にコルーチンを作成します
  • $worker->eventLoop=xxx;を利用して、異なるworkerに異なるコルーチンドライバを設定することができます
<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Events\Fiber;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

$worker1 = new Worker('http://0.0.0.0:8001');
$worker1->eventLoop = Swoole::class; // Swooleコルーチンを使用
$worker1->onMessage = function (TcpConnection $connection, Request $request) {
    Coroutine::create(function () {
        echo file_get_contents("http://www.example.com/event/notify");
    });
    $connection->send('ok');
};

$worker2 = new Worker('http://0.0.0.0:8001');
$worker2->eventLoop = Fiber::class; // 自身が持つFiberコルーチンを使用
$worker2->onMessage = function (TcpConnection $connection, Request $request) {
    Coroutine::create(function () {
        echo file_get_contents("http://www.example.com/event/notify");
    });
    $connection->send('ok');
};

Worker::runAll();

コルーチンが提供するインターフェース

interface CoroutineInterface
{

    /**
     * コルーチンを作成し、即座に実行します
     */
    public static function create(callable $callable, ...$data): CoroutineInterface;

    /**
     * コルーチンの実行を開始します
     */
    public function start(mixed ...$args): mixed;

    /**
     * コルーチンの実行を再開します
     */
    public function resume(mixed ...$args): mixed;

    /**
     * コルーチンのIDを取得します
     */
    public function id(): int;

    /**
     * コルーチンの破棄時のコールバックを設定します
     */
    public static function defer(callable $callable): void;

    /**
     * 現在のコルーチンを一時停止します
     */
    public static function suspend(mixed $value = null): mixed;

    /**
     * 現在のコルーチンを取得します
     */
    public static function getCurrent(): CoroutineInterface|Fiber|SwowCoroutine|static;

    /**
     * 現在がコルーチン環境であるかどうかを判断します
     */
    public static function isCoroutine(): bool;

}

コルーチンについて

利点

PHPがコルーチンを導入した最大の効果は、同期的な方法で非同期コードを書くことができることであり、コールバック地獄を避け、コードの可読性と保守性が向上しました。
コルーチンは、IO集約型ビジネスの弾力性を大幅に向上させ、少ないプロセスでより大きなスループットを提供できます。

欠点

しかし、コルーチンを導入すると、開発者はグローバル変数の汚染、リソース競合、サードパーティライブラリの改造などの問題に常に注意を払う必要があり、開発と保守のコストが増加し、精神的負担も明らかに増します。

コルーチンを導入すると、コルーチンの作成、スケジューリング、破棄、接続プールなどの追加のオーバーヘッドが発生します。
大量の負荷テストデータによると、CPUを十分に活用する場合、コルーチンを導入すると最大性能はブロッキングIOに比べて約10%-20%低下します。