Последнее обновление:2026-10-12
Практика с channel в C++20¶
Практика C++20: channel¶
Продолжая два предыдущих прямых эфира, где разбирались применение и внутренняя реализация thread, jthread, stop_token, stop_source, stop_callback, cv, cv_any и других, — как на основе этих знаний реализовать небольшой проект?
Так родилась эта статья: написать свой channel.
1.Дизайн¶
1.1 Проектирование интерфейса¶
На уровне интерфейса мы хотим, чтобы он был как у обычного channel: чтобы можно было за один раз создать channel для отправки и получения.
Например:
Реализовать это можно, предоставив по аналогии с make_unique внешний интерфейс; возвращаемое значение получается через структурированные привязки C++17 — так мы получаем два channel.
Два ключевых интерфейса: отправка и получение.
- Отправка
- Получение
Помимо этого, можно предоставить интерфейс shutdown, вызываемый в деструкторе, чтобы при блокировке send и recv их можно было разбудить и освободить.
Например:
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 Проектирование внутренней очереди¶
Внутреннюю очередь можно спроектировать по аналогии с опубликованным несколькими днями ранее потокобезопасным чередом многопоточных производителей и потребителей: внутри можно хранить state, включающий queue, mutex и cv_any.
struct State {
std::queue<T> queue;
std::mutex mutex;
// C++11 condition_variable_any
std::condition_variable_any cv;
};
2.Использование¶
Использовать очень просто: достаточно создать make'ом два channel, а затем вызывать send и recv.
Здесь я использовал jthread + stop_token для этой работы, например:
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);
Принимающий поток устроен аналогично; когда выполнение почти завершено, мы можем вызвать
Так мы смоделировали channel: отправляющая сторона посылает данные, принимающая — получает их.
Пример запуска:
➜ channel ./a.out
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9
Похожие темы¶
- Введение в корутины C++20 | C++那些事
- Концепции Concepts в C++20 | C++那些事
- constexpr и оператор «запятая» в C++20 | C++那些事
- Порядок байтов endian в C++20 | C++那些事
- Лямбда-выражения в C++20 (часть 1) | C++那些事
- Модули Modules в C++20 | C++那些事
- Библиотека Ranges в C++20 | C++那些事
- Дизайн и подводные камни std::span в C++20 | C++那些事
- Обзор новых возможностей C++20 | C++那些事
- [[likely]] и [[unlikely]] в C++20 | C++那些事