Ir para o conteúdo

Última atualização:2026-10-12

Prática com channel no C++20

Prática do C++20: channel

Após as duas lives anteriores, que explicaram o uso e a implementação subjacente de thread, jthread, stop_token, stop_source, stop_callback, cv, cv_any, etc., como construir um pequeno projeto com base nesses conhecimentos?

Daí nasceu este artigo: escrever um channel.

1.Design

1.1 Design da interface

No nível da interface, esperamos que seja como um channel de uso geral, capaz de criar de uma só vez um channel de envio e um de recebimento.

Por exemplo:

auto [sender, receiver] = make_channel<int>();

A implementação aqui pode fornecer uma interface externa semelhante a make_unique, e o valor de retorno é obtido por meio das vinculações estruturadas do C++17; assim, obtemos dois channels.

As duas interfaces mais centrais: envio e recebimento.

  • Envio
 sender.send(counter++);
  • Recebimento
auto value = receiver.recv();

Além disso, podemos fornecer uma interface shutdown no destrutor, para que send e recv bloqueados possam ser despertados e liberados.

Por exemplo:

bool shutdown() {
    if (!state_) {
        return false;
    }

    light_city_lock lk(state_->mutex);
    if (source_.stop_requested()) {
        return false;
    }
    source_.request_stop();
    state_->cv.notify_all();
    return true;
}

1.2 Design da fila interna

Para a fila interna, podemos nos referir ao artigo publicado há alguns dias sobre como desenhar uma fila thread-safe com múltiplos produtores e múltiplos consumidores; aqui podemos manter internamente um state, acompanhado de queue, mutex e cv_any.

struct State {
    std::queue<T> queue;
    std::mutex mutex;
    // C++11 condition_variable_any
    std::condition_variable_any cv;
};

2.Uso

O uso é bem fácil: basta fazer (make) dois channels e, então, enviar com send e receber com recv.

Aqui usei jthread + stop_token para realizar esse trabalho, por exemplo:

std::jthread sender_thread(
    [](Channel<int> sender, std::stop_token token) {
      int counter = 0;
      while (!token.stop_requested()) {
        sender.send(counter++);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
    },
    std::move(sender), token);

A thread receptora é semelhante; quando a execução estiver quase no fim, podemos usar

stop_source.request_stop();

Com isso, simulamos um channel: o lado que envia transmite os dados e o lado que recebe os obtém.

Exemplo de execução:

➜  channel ./a.out                  
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9

Tópicos relacionados

Comentários