Saltar a contenido

Última actualización:2026-10-12

C++20 channel en la práctica

Channel en la práctica con C++20

Continuando con las dos sesiones en directo anteriores, donde explicamos el uso y la implementación subyacente de thread, jthread, stop_token, stop_source, stop_callback, cv, cv_any, etc., ¿cómo construir un pequeño proyecto a partir de esos conocimientos?

De ahí surge esta entrega: escribir un channel.

1.Diseño

1.1 Diseño de la interfaz

En cuanto a la interfaz, esperamos que sea como un channel genérico, capaz de crear de una sola vez un channel de envío y de recepción.

Por ejemplo:

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

La implementación puede ofrecer una interfaz hacia el exterior similar a la de make_unique, y el valor de retorno se obtiene mediante las vinculaciones estructuradas de C++17; con esto ya tenemos dos channel.

Las dos interfaces esenciales: enviar y recibir.

  • Enviar
 sender.send(counter++);
  • Recibir
auto value = receiver.recv();

Además, se puede proporcionar una interfaz shutdown en el destructor, de modo que cuando send o recv estén bloqueados puedan despertarse y liberarse.

Por ejemplo:

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 Diseño de la cola interna

Para la cola interna puede tomarse como referencia lo publicado hace unos días sobre cómo diseñar una cola thread-safe de múltiples productores y consumidores; aquí podemos mantener internamente un state, junto con queue, mutex y cv_any.

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

2.Uso

Su uso es muy sencillo: basta con hacer (make) dos channel y luego send y recv.

Aquí he usado jthread + stop_token para este trabajo, por ejemplo:

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);

El hilo receptor es parecido; cuando la ejecución está a punto de terminar, podemos usar

stop_source.request_stop();

Con esto queda simulado un channel: el extremo emisor envía datos y el extremo receptor los recibe.

Ejemplo de ejecución:

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

Temas relacionados

Comentarios