Coroutines

Le coroutines sono un meccanismo di concorrenza a livello utente più leggero rispetto ai thread, che consente la programmazione multi-tasking all'interno di un processo. Realizzano il cambio tra coroutines attraverso il controllo manuale della sospensione e del ripristino, evitando il costo del cambio di contesto del processo. Workerman fornisce un'interfaccia generica per le coroutines, compatibile automaticamente con i driver Swoole/Swow/Fiber.

Suggerimento
Questa funzionalità richiede workerman>=5.1.0

Attenzione

  • Le coroutines supportano solo i driver Swoole, Swow, Fiber
  • Se si utilizza il driver Fiber, è necessario installare composer require revolt/event-loop
  • I driver Swoole o Swow possono automatizzare la coroutine per le funzioni di blocco di PHP, consentendo l'esecuzione asincrona del codice originariamente sincrono
  • Tuttavia, Fiber non può automatizzare le coroutine come Swoole e Swow; quando incontra funzioni di blocco fornite da PHP, blocca l'intero processo e non avviene alcun cambio di coroutine
  • Quando si utilizzano i driver Swoole, Swow, Fiber, workerman crea automaticamente una coroutine ad ogni chiamata delle callback onWorkerStart, onMessage, onConnect, onClose, ecc.
  • È possibile utilizzare $worker->eventLoop=xxx; per impostare driver di coroutine diversi per diversi 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; // Utilizza 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; // Utilizza la coroutine Fiber integrata
$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();

Interfaccia fornita dalle coroutines

interface CoroutineInterface
{

    /**
     * Crea una coroutine ed eseguila immediatamente
     */
    public static function create(callable $callable, ...$data): CoroutineInterface;

    /**
     * Inizia l'esecuzione della coroutine
     */
    public function start(mixed ...$args): mixed;

    /**
     * Riprendi l'esecuzione della coroutine
     */
    public function resume(mixed ...$args): mixed;

    /**
     * Ottieni l'id della coroutine
     */
    public function id(): int;

    /**
     * Imposta il callback da eseguire al momento della distruzione della coroutine
     */
    public static function defer(callable $callable): void;

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

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

    /**
     * Verifica se si è in un ambiente di coroutine
     */
    public static function isCoroutine(): bool;

}

Informazioni sulle coroutines

Vantaggi

L'introduzione delle coroutines in PHP consente di scrivere codice asincrono in modo sincrono, evitando il "callback hell", migliorando la leggibilità e la manutenibilità del codice. Le coroutines possono migliorare significativamente la resilienza delle operazioni I/O intensive, consentendo a meno processi di fornire una maggiore capacità di throughput.

Svantaggi

Tuttavia, con l'introduzione delle coroutines, gli sviluppatori devono prestare attenzione alla contaminazione delle variabili globali, alla concorrenza delle risorse, alla modifica delle librerie di terze parti, e così via, il che aumenta il costo di sviluppo e manutenzione e aumenta significativamente il carico mentale.

L'introduzione delle coroutines comporta costi aggiuntivi per la creazione, la pianificazione, la distruzione delle coroutines e il pooling delle connessioni. Dall'analisi dei dati sui test di carico, si osserva che, in condizioni di utilizzo ottimale della CPU, le performance massime con l'introduzione delle coroutines possono diminuire di circa il 10%-20% rispetto all'I/O bloccante.