Last updated:2026-10-12
C++20 channel in Practice¶
C++20 in Practice: channel¶
In the previous two live streams we covered the usage and underlying implementation of thread, jthread, stop_token, stop_source, stop_callback, cv, cv_any, and more. So how can we turn this knowledge into a small project?
Which brings us to this article: writing a channel.
1. Design¶
1.1 Interface Design¶
At the interface level we expect the same as a general-purpose channel: creating a sending and a receiving channel in one shot.
For example:
The implementation can expose an outward-facing interface similar to make_unique; the return values are obtained via C++17 structured bindings, and at that point we have two channels.
The two most essential interfaces: send and receive.
- Send
- Receive
Besides that, a shutdown interface can be provided at destruction time, so that blocked send/recv calls can be woken up and released.
For example:
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 Internal Queue Design¶
For the internal queue, refer to the article published a few days ago on designing a thread-safe multi-producer multi-consumer queue: here we can maintain a state internally that carries a queue, a mutex, and a cv_any.
struct State {
std::queue<T> queue;
std::mutex mutex;
// C++11 condition_variable_any
std::condition_variable_any cv;
};
2. Usage¶
It is very easy to use: just make the two channels, then send and recv.
Here I used jthread + stop_token to do the work, for example:
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);
The receiving thread is similar. Once things have run about long enough, we can use
At this point a channel has been simulated: the sending side sends data, and the receiving side just receives it.
Sample run:
➜ channel ./a.out
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9
Related Topics¶
- Getting Started with C++20 Coroutines | C++那些事
- C++20 Concepts | C++那些事
- C++20 constexpr and the Comma Expression | C++那些事
- C++20 Byte Order (endian) | C++那些事
- C++20 Lambda Expressions (Part 1) | C++那些事
- C++20 Modules | C++那些事
- C++20 Ranges Library | C++那些事
- C++20 std::span Design and Pitfalls | C++那些事
- A Guide to C++20 New Features | C++那些事
- C++20 [[likely]] and [[unlikely]] | C++那些事