Closing Channels in C++
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
// In this example we'll use a `jobs` queue to communicate work to be done
// from the main thread to a worker thread. When we have no more jobs for
// the worker we'll signal the end of work.
int main() {
std::queue<int> jobs;
std::mutex mtx;
std::condition_variable cv;
bool done = false;
bool all_jobs_sent = false;
// Here's the worker thread. It repeatedly receives from `jobs`.
// We use a condition variable to notify when we've worked all our jobs.
std::thread worker([&]() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&]{ return !jobs.empty() || all_jobs_sent; });
if (!jobs.empty()) {
int j = jobs.front();
jobs.pop();
lock.unlock();
std::cout << "received job " << j << std::endl;
} else if (all_jobs_sent) {
std::cout << "received all jobs" << std::endl;
done = true;
cv.notify_one();
return;
}
}
});
// This sends 3 jobs to the worker over the `jobs` queue,
// then signals that all jobs are sent.
for (int j = 1; j <= 3; ++j) {
{
std::lock_guard<std::mutex> lock(mtx);
jobs.push(j);
}
cv.notify_one();
std::cout << "sent job " << j << std::endl;
}
{
std::lock_guard<std::mutex> lock(mtx);
all_jobs_sent = true;
}
cv.notify_one();
std::cout << "sent all jobs" << std::endl;
// We await the worker using the condition variable.
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&]{ return done; });
}
// In C++, we don't have a direct equivalent to checking if a channel is closed.
// Instead, we can check if the queue is empty and all jobs have been sent.
bool more_jobs = !jobs.empty() || !all_jobs_sent;
std::cout << "received more jobs: " << (more_jobs ? "true" : "false") << std::endl;
worker.join();
return 0;
}
To compile and run this program:
$ g++ -std=c++11 -pthread closing_channels.cpp -o closing_channels
$ ./closing_channels
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false
In this C++ version, we use a std::queue
to represent the channel of jobs. We use a mutex and a condition variable to synchronize access to the queue and to signal between threads.
The close
operation on a channel is simulated by setting a flag all_jobs_sent
to true. The worker thread checks this flag to know when to stop waiting for new jobs.
The concept of receiving from a closed channel is approximated by checking if the queue is empty and all jobs have been sent. This is not a perfect analogy, but it serves a similar purpose in this context.
Note that C++ doesn’t have built-in constructs that directly correspond to Go’s channels and goroutines. Instead, we use standard library components like std::thread
, std::mutex
, and std::condition_variable
to achieve similar functionality.