Coroutines

Les coroutines sont un mécanisme de concurrence léger au niveau des utilisateurs, plus léger que les threads, permettant une planification multitâche au sein d'un processus. Elles réalisent des changements entre coroutines par un contrôle manuel de la suspension et de la reprise, évitant ainsi le coût des changements de contexte de processus. Workerman fournit une interface de coroutine générique, compatible automatiquement avec les pilotes Swoole/Swow/Fiber en sous-jacent.

Remarque
Cette fonctionnalité nécessite workerman>=5.1.0

Attention

  • Les coroutines ne supportent que les pilotes Swoole, Swow, Fiber
  • Si vous utilisez le pilote Fiber, il est nécessaire d'installer composer require revolt/event-loop
  • Les pilotes Swoole ou Swow peuvent transformer automatiquement les fonctions bloquantes PHP en coroutines, permettant ainsi l'exécution asynchrone du code original synchronisé
  • Toutefois, Fiber ne peut pas effectuer cette transformation automatique comme Swoole et Swow, et lorsqu'il rencontre des fonctions bloquantes fournies par PHP, il bloque tout le processus sans effectuer de changement de coroutine
  • Lorsque vous utilisez les pilotes Swoole, Swow ou Fiber, workerman créera automatiquement une coroutine à chaque exécution des rappels onWorkerStart, onMessage, onConnect, onClose, etc.
  • Vous pouvez utiliser $worker->eventLoop=xxx; pour définir des pilotes de coroutine différents pour différents workers
<?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; // Utiliser la coroutine 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; // Utiliser la coroutine Fiber intégrée
$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 fournie par les coroutines

interface CoroutineInterface
{

    /**
     * Créer une coroutine et l'exécuter immédiatement
     */
    public static function create(callable $callable, ...$data): CoroutineInterface;

    /**
     * Démarrer l'exécution de la coroutine
     */
    public function start(mixed ...$args): mixed;

    /**
     * Reprendre l'exécution de la coroutine
     */
    public function resume(mixed ...$args): mixed;

    /**
     * Obtenir l'ID de la coroutine
     */
    public function id(): int;

    /**
     * Définir le rappel lors de la destruction de la coroutine
     */
    public static function defer(callable $callable): void;

    /**
     * Suspendre la coroutine actuelle
     */
    public static function suspend(mixed $value = null): mixed;

    /**
     * Obtenir la coroutine actuelle
     */
    public static function getCurrent(): CoroutineInterface|Fiber|SwowCoroutine|static;

    /**
     * Vérifier si l'environnement est celui d'une coroutine
     */
    public static function isCoroutine(): bool;

}

À propos des coroutines

Avantages

L'introduction des coroutines en PHP permet d'écrire du code asynchrone de manière synchrone, évitant ainsi le "callback hell", et améliore la lisibilité et la maintenabilité du code.
Les coroutines peuvent considérablement améliorer la flexibilité des applications à forte intensité d'E/S, permettant de fournir un débit plus élevé avec moins de processus.

Inconvénients

Cependant, l'introduction des coroutines exige que les développeurs soient constamment attentifs à la pollution des variables globales, à la concurrence des ressources et à la modification des bibliothèques tierces, augmentant ainsi les coûts de développement et la charge cognitive.

L'introduction des coroutines entraîne des coûts supplémentaires tels que la création, la planification, la destruction des coroutines et les pools de connexions.
D'après de nombreuses données de tests de charge, dans des conditions d'utilisation optimale du CPU, l'introduction de coroutines diminue les performances maximales d'environ 10 % à 20 % par rapport à l'E/S bloquante.