Utilizando e Compreendendo Corrotinas no Hyperf

Corrotinas oferecem uma abordagem de concorrência leve, gerenciada no espaço do usuário em vez de depender do escalonador do kernel do sistema operacional. Isso permite uma execução mais eficiente e menos sobrecarga.

Maneiras de Criar Corrotinas

Função co()

A função co() é um atalho conveniente para iniciar uma nova corrotina.


public function test() {
    // Obtém e exibe o ID da corrotina atual (principal).
    echo "ID inicial: " . Coroutine::id() . PHP_EOL;

    // Cria e executa uma nova corrotina.
    co(function () {
        // Obtém e exibe o ID da nova corrotina.
        echo "ID da corrotina: " . Coroutine::id() . PHP_EOL;
        echo "Esta corrotina foi criada usando co()".PHP_EOL;
    });
}

Acessando o endpoint /index/test via HTTP:


http://seu_ip:9501/index/test

Saída esperada no terminal:


ID inicial: 2
ID da corrotina: 3
Esta corrotina foi criada usando co()

Função go()

Similar à função co(), go() também inicia uma nova corrotina.


public function test() {
    echo "ID principal: " . Coroutine::id() . PHP_EOL;

    // Cria e executa uma nova corrotina.
    go(function () {
        echo "ID da corrotina: " . Coroutine::id() . PHP_EOL;
        echo "Esta corrotina foi criada usando go()".PHP_EOL;
    });
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal:


ID principal: 2
ID da corrotina: 3
Esta corrotina foi criada usando go()

Método Coroutine::create()

Para um controle mais explícito, o método estático Coroutine::create() pode ser utilizado.


use Hyperf\Utils\Coroutine;

public function test() {
    echo "ID da corrotina principal: " . Coroutine::id() . PHP_EOL;

    // Cria uma nova corrotina de forma explícita.
    Coroutine::create(function() {
        echo "ID da sub-corrotina: " . Coroutine::id() . PHP_EOL;
        echo "Esta corrotina foi criada usando Coroutine::create()".PHP_EOL;
    });
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal:


ID da corrotina principal: 2
ID da sub-corrotina: 3
Esta corrotina foi criada usando Coroutine::create()

Funções Úteis de Corrotina

Verificar se o código atual está sendo executado dentro de um ambiente de corrotina:


Hyperf\Utils\Coroutine::inCoroutine(): bool

Obter o identificador único da corrotina atual:


Hyperf\Utils\Coroutine::id()

Canal (Channel) para Comnuicação

O Channel é fundamental para a comunicação e sincronização entre corrotinas.


public function test() {
    co(function () {
        // Cria um canal para comunicação.
        $canal = new \Swoole\Coroutine\Channel();

        // Inicia uma corrotina filha para enviar dados ao canal.
        co(function () use ($canal) {
            $canal->push('Dados enviados pela corrotina filha');
        });

        // Aguarda e recupera dados do canal.
        $dadosRecebidos = $canal->pop();
        echo "Dados recebidos da corrotina filha: " . $dadosRecebidos;
    });
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada:


Dados recebidos da corrotina filha: Dados enviados pela corrotina filha

Característica defer

O defer permite agendar a execução de uma função após a conclusão da corrotina atual, de forma semelhante a um bloco finally.


public function test() {
    Coroutine::defer(function() {
        echo "Primeira ação adiada".PHP_EOL;
    });
    Coroutine::defer(function() {
        echo "Segunda ação adiada".PHP_EOL;
    });
    Coroutine::defer(function() {
        echo "Terceira ação adiada".PHP_EOL;
    });
    echo 'Execução principal da corrotina'.PHP_EOL;
}

Acessando o endopint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal (a ordem das ações adiadas é invertida):


Execução principal da corrotina
Terceira ação adiada
Segunda ação adiada
Primeira ação adiada

Característica WaitGroup

WaitGroup é usado para aguardar a conclusão de um conjunto de corrotinas.


use Hyperf\Utils\WaitGroup;

public function test() {
    $wg = new WaitGroup();
    // Incrementa o contador para duas corrotinas.
    $wg->add(2);

    // Corrotina A: simula uma tarefa demorada.
    co(function () use ($wg) {
           $tempoEspera = mt_rand(1, 5);
           sleep($tempoEspera);
           echo "Corrotina A concluída".PHP_EOL;
           // Decrementa o contador ao concluir.
           $wg->done();
    });

    // Corrotina B: simula outra tarefa demorada.
    co(function () use ($wg) {
           $tempoEspera = mt_rand(1, 5);
           sleep($tempoEspera);
           echo "Corrotina B concluída".PHP_EOL;
           // Decrementa o contador ao concluir.
           $wg->done();
    });

    // Aguarda até que ambas as corrotinas chamem done().
    $wg->wait();
    echo "Todas as corrotinas foram executadas".PHP_EOL;
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal (a ordem de conclusão das corrotinas pode variar):


CorrotinaB concluída
CorrotinaA concluída
Todas as corrotinas foram executadas

Característica Parallel

A classe Parallel permite executar múltiplas corrotinas concorrentemente e coletar seus resultados.


use Hyperf\Utils\Exception\ParallelExecutionException;
use Hyperf\Utils\Coroutine;
use Hyperf\Utils\Parallel;

public function test() {
    $parallel = new Parallel();

    // Adiciona a primeira tarefa paralela.
    $parallel->add(function () {
        $tempoEspera = mt_rand(1, 5);
        sleep($tempoEspera);
        echo "Tarefa A concluída".PHP_EOL;
        return Coroutine::id(); // Retorna o ID da corrotina.
    });

    // Adiciona a segunda tarefa paralela.
    $parallel->add(function () {
        $tempoEspera = mt_rand(1, 5);
        sleep($tempoEspera);
        echo "Tarefa B concluída".PHP_EOL;
        return Coroutine::id(); // Retorna o ID da corrotina.
    });

    try {
        // Aguarda a conclusão de todas as tarefas e coleta os resultados.
        $resultados = $parallel->wait();
        echo "Resultados da execução paralela:".PHP_EOL;
        var_dump($resultados);
    } catch(ParallelExecutionException $e) {
        // Captura exceções que possam ocorrer durante a execução paralela.
        var_dump($e->getResults()); // Resultados das tarefas concluídas antes da exceção.
        var_dump($e->getThrowables()); // Exceções lançadas pelas corrotinas.
    }
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal (a ordem de conclusão pode variar):


Tarefa B concluída
Tarefa A concluída
Resultados da execução paralela:
array(2) {
  [1]=>
  int(4)
  [0]=>
  int(3)
}

Uma versão abreviada usando a função helper parallel():


$resultados = parallel([
    function () {
        $tempoEspera = mt_rand(1, 5);
        sleep($tempoEspera);
        echo "Tarefa A concluída".PHP_EOL;
        return Coroutine::id();
    },
    function () {
        $tempoEspera = mt_rand(1, 5);
        sleep($tempoEspera);
        echo "Tarefa B concluída".PHP_EOL;
        return Coroutine::id();
    }
]);

Contexto de Corrotina

O contexto de corrotina permite armazenar e recuperar dados associados à corrotina atual de forma segura.


use Hyperf\Context\Context;

public function test() {
    co(function() {
        // Define um valor no contexto da corrotina.
        Context::set('nome', 'Valor Exemplo');
        // Recupera o valor do contexto.
        $nome = Context::get('nome');
        echo $nome . PHP_EOL;
    });
}

Acessando o endpoint /index/test:


http://seu_ip:9501/index/test

Saída esperada no terminal:


Valor Exemplo

Tags: hyperf corrotinas swoole Concorrência programação assíncrona

Publicado em 7-31 16:14