Waitgroups in C++
Our program demonstrates how to wait for multiple threads to finish using a C++ implementation similar to Go’s WaitGroup.
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
#include <mutex>
#include <condition_variable>
class WaitGroup {
private:
std::mutex mutex;
std::condition_variable cv;
int count = 0;
public:
void add(int n) {
std::unique_lock<std::mutex> lock(mutex);
count += n;
}
void done() {
std::unique_lock<std::mutex> lock(mutex);
if (--count == 0) {
cv.notify_all();
}
}
void wait() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this] { return count == 0; });
}
};
// This is the function we'll run in every thread.
void worker(int id) {
std::cout << "Worker " << id << " starting\n";
// Sleep to simulate an expensive task.
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Worker " << id << " done\n";
}
int main() {
// This WaitGroup is used to wait for all the
// threads launched here to finish.
WaitGroup wg;
// Launch several threads and increment the WaitGroup
// counter for each.
for (int i = 1; i <= 5; i++) {
wg.add(1);
// Wrap the worker call in a lambda that makes sure to tell
// the WaitGroup that this worker is done. This way the worker
// itself does not have to be aware of the concurrency primitives
// involved in its execution.
std::thread([&wg, i]() {
worker(i);
wg.done();
}).detach();
}
// Block until the WaitGroup counter goes back to 0;
// all the workers notified they're done.
wg.wait();
// Note that this approach has no straightforward way
// to propagate errors from workers. For more
// advanced use cases, consider using other synchronization
// primitives or libraries that support error handling.
}
To compile and run the program:
$ g++ -std=c++11 -pthread waitgroups.cpp -o waitgroups
$ ./waitgroups
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done
The order of workers starting up and finishing is likely to be different for each invocation.
In this C++ version, we’ve created a WaitGroup
class that mimics the functionality of Go’s sync.WaitGroup
. The worker
function remains largely the same, but we use std::thread
to create our threads instead of goroutines.
The main
function structure is similar, but we use C++ lambda functions to wrap our worker calls. We also use detach()
on our threads to allow them to run independently.
Note that C++ doesn’t have built-in goroutines or a direct equivalent to Go’s WaitGroup, so we’ve implemented a simple version using standard C++ threading primitives. For more complex scenarios, you might want to consider using more robust threading libraries or C++20’s coroutines for a closer approximation of Go’s concurrency model.