I'm trying to lock my list of mutexes in the following code so that only one thread can search it, unlock, lock or modify it at a time.
#include <mutex>
#include <map>
#include <memory>
#include <vector>
#include <thread>
#include <atomic>
#include <iostream>
#include <Windows.h>
struct MoveableMutex
{
std::mutex m;
MoveableMutex() {}
MoveableMutex(MoveableMutex const&) {}
MoveableMutex& operator = (MoveableMutex const&) { return *this; }
};
class Locks
{
private:
static std::mutex map_lock;
static std::uint32_t lock_count;
std::map<std::uint32_t, MoveableMutex> locklist;
public:
std::uint32_t AddLock();
void RemoveLock(std::uint32_t ID);
void Lock(std::uint32_t ID);
bool TryLock(std::uint32_t ID);
void Unlock(std::uint32_t ID);
};
std::uint32_t Locks::lock_count = 0;
std::mutex Locks::map_lock;
std::uint32_t Locks::AddLock()
{
std::lock_guard<std::mutex> guard(map_lock);
locklist.insert(std::make_pair(++lock_count, MoveableMutex()));
return lock_count;
}
void Locks::RemoveLock(std::uint32_t ID)
{
std::lock_guard<std::mutex> guard(map_lock);
auto it = locklist.find(ID);
if (it != locklist.end())
{
it->second.m.unlock();
locklist.erase(it);
}
}
void Locks::Lock(std::uint32_t ID)
{
std::lock_guard<std::mutex> guard(map_lock);
auto it = this->locklist.find(ID);
if (it != this->locklist.end())
{
it->second.m.lock();
}
}
bool Locks::TryLock(std::uint32_t ID)
{
std::lock_guard<std::mutex> guard(map_lock);
auto it = this->locklist.find(ID);
if (it != this->locklist.end())
{
return it->second.m.try_lock();
}
return false;
}
void Locks::Unlock(std::uint32_t ID)
{
std::lock_guard<std::mutex> guard(map_lock);
auto it = this->locklist.find(ID);
if (it != locklist.end())
{
it->second.m.unlock();
}
}
int main()
{
Locks locklist;
int i = locklist.AddLock();
std::atomic<bool> stop(false);
std::atomic<bool> stop2(false);
std::thread o([&]
{
locklist.Lock(i);
while(!stop)
{
std::cout << "Hey\n";
Sleep(100);
}
locklist.Unlock(i);
});
std::thread t([&]
{
locklist.Lock(i);
while(!stop2)
{
std::cout << "Hey2\n";
Sleep(100);
}
locklist.Unlock(i);
});
Sleep(1000);
stop = true;
system("CLS");
o.join();
Sleep(1000);
stop2 = true;
t.join();
return 0;
}
However, with the std::lock_guard inside the Unlock function, it causes a deadlock. If I remove the lock_guard from the Unlock function, it works fine.
Is there a reason the lock_guard isn't destructing or unlocking?
One thread calls Lock, which ends up locking the mutex in the map. The other thread calls Lock, which locks map_lock then tries to lock the mutex in the map, and gets stuck there (with map_lock still held). Eventually, the first thread gets out of the loop and calls Unlock, which gets stuck waiting on map_lock.
The main design flaw here is that you have a thread acquire two locks, one after another. This only works safely if all threads acquire them in the same order (and release in reverse order of acquiring). But your code acquires them in different order at different times: that's a recipe for a deadlock.
See also: lock hierarchy
Related
I'm writing a program whose main thread spawns a worker thread that performs some work, sleeps for a set amount of time in an infinite loop, i.e. the worker thread executes:
void do_work() {
for (;;) {
// do some work
std::this_thread::sleep_for(100ms);
}
}
Now, I would additionally like to be able to temporarily completely disable this worker thread from the main thread, i.e. I would like to write the following functions:
disable_worker(): disable the worker thread
enable_worker(): enable the worker thread again
What I've come up with is the following:
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std::literals::chrono_literals;
bool enabled;
std::mutex mtx;
std::condition_variable cond;
void disable_worker() {
std::lock_guard<std::mutex> lock(mtx);
enabled = false;
}
void enable_worker() {
{
std::lock_guard<std::mutex> lock(mtx);
enabled = true;
}
cond.notify_one();
}
void do_work() {
for (;;) {
std::unique_lock<std::mutex> lock(mtx);
cond.wait(lock, []{ return enabled; });
// ... do some work ...
std::this_thread::sleep_for(100ms);
}
}
int main() {
std::thread t(do_work);
// ... enable/disable t as necessary ...
}
I suppose this works (at least I can't spot any issues), however, I would also like to guarantee that when either of enable_worker and disable_worker return (in the main thread), the working thread is guaranteed to be either blocking on the condition variable or sleeping, i.e. not performing any work. How can I implement this without any race conditions?
Here is an API for a concurrent door with a queue counter and a the idea of using it "sleepily".
struct SleepyDoorQueue {
void UseDoor() {
auto l = lock();
++queue_size;
cv.notify_all();
cv.wait( l, [&]{ return open; } );
--queue_size;
}
// sleeps for a while, then tries to open the door.
// considered in queue while sleeping.
template<class Rep, class Period>
void SleepyUseDoor( const std::chrono::duration<Rep, Period>& rel_time ) {
{
auto l = lock();
++queue_size;
cv.notify_all();
}
std::this_thread::sleep_for(rel_time);
auto l = lock();
cv.wait( l, [&]{ return open; } );
--queue_size;
}
void CloseDoor() {
auto l = lock();
open = false;
}
void OpenDoor() {
auto l = lock();
open = true;
cv.notify_all();
}
void WaitForQueueSize(std::size_t n) const {
auto l = lock();
cv.wait(l, [&]{ return queue_size >= n; } );
}
explicit SleepyDoorQueue( bool startOpened = true ):open(startOpened) {}
private:
std::condition_variable cv;
mutable std::mutex m;
std::size_t queue_size = 0;
bool open = true;
auto lock() const { return std::unique_lock(m); }
};
the main thread closes the door, and waits for a queue size of 1 to ensure that the worker thread isn't working.
The worker thread does a SleepyUseDoor to try to open it after sleeping for 100ms.
When the worker thread can do work, the main thread just opens the door.
This will be inefficient with a large number of worker and controller threads, as I use the same cv for both the queue and door opening message. So one will cause the other threads to wake up spuriously. With one worker and one controller thread, the messages won't be spurious to any significant degree.
I only notify on queue size increase and door opening, but I do more than 1 notification on purpose (if there is someone waiting for a queue size change and a door opener eats it, that would suck).
You could probably implement this with two doors actually.
struct Door {
// blocks until the door is open
void UseDoor() const {
auto l = lock();
cv.wait(l, [&]{ return open; });
}
// opens the door. Notifies blocked threads trying to use the door.
void OpenDoor() {
auto l = lock();
open = true;
cv.notify_all();
}
// closes the door.
void CloseDoor() {
auto l = lock();
open = false;
}
explicit Door(bool startOpen=true):open(startOpen) {}
private:
std::condition_variable cv;
mutable std::mutex m;
bool open = true;
auto lock() const { return std::unique_lock(m); }
};
The worker thread does this:
Door AmNotWorking(true);
Door CanWork(true);
void work() {
for(;;) {
canWork.UseDoor()
AmNotWorking.CloseDoor();
// work
AmNotWorking.OpenDoor();
std::this_thread::sleep_for(100ms);
}
}
the controller thread does:
void preventWork() {
CanWork.CloseDoor();
AmNotWorking.UseDoor();
}
void allowWork() {
CanWork.OpenDoor();
}
but I see a race condition there; between CanWork.UseDoor() and AmNotWorking.OpenDoor(); someone could close the CanWork door then read the AmNotWorking door. We need that to be atomic.
// Goes through the door when it is open.
// atomically runs the lambda passed in while the
// mutex is locked with checking the door state.
// WARNING: this can cause deadlocks if you do the
// wrong things in the lambda.
template<class F>
void UseDoor(F atomicWhenOpen) const {
auto l = lock();
cv.wait(l, [&]{ return open; });
atomicWhenOpen();
}
that does an atomic operation when we successfully use the door. A bit dangerous, but the worker thread can now:
void work() {
for(;;) {
canWork.UseDoor([]{AmNotWorking.CloseDoor();});
// work
AmNotWorking.OpenDoor();
std::this_thread::sleep_for(100ms);
}
}
this guarantees we have the "AmNotWorking" door closed in the same lock as we verified the "CanWork" door is open.
void preventWork() {
CanWork.CloseDoor();
AmNotWorking.UseDoor();
}
If the "use can work and close am working" operation happens before the CanWork.CloseDoor(), we won't be able to AmNotWorking.UseDoor() until the worker thread finishes their work.
If it happens after CanWork.CloseDoor(), then the AmNotWorking.UseDoor() is closed, so we again wait until the worker thread is not working.
We can't CanWork.CloseDoor() between the can work door being used and the AmNotWorking being closed, which is what that extra atomic lambda callback gives us.
We can probably make a less dangerous primitive, but I'm not sure how to do it elegantly.
Maybe a simple semaphore?
template<class T = std::ptrdiff_t>
struct Semaphore {
void WaitUntilExactValue( T t ) const {
auto l = lock();
cv.wait( l, [&]{ return value==t; }
}
void WaitUntilAtLeastValue( T t ) const {
auto l = lock();
cv.wait( l, [&]{ return value>=t; }
}
void WaitUntilAtMostValue( T t ) const {
auto l = lock();
cv.wait( l, [&]{ return value<=t; }
}
void Increment() {
auto l = lock();
++value;
cv.notify_all();
}
void BoundedIncrement(T ceil) {
auto l = lock();
cv.wait(l, [&]{ return value+1 <= ceil; });
++value;
cv.notify_all();
}
void Decrement() {
auto l = lock();
--value;
cv.notify_all();
}
void BoundedDecrement(T floor) {
auto l = lock();
cv.wait(l, [&]{ return value-1 >= floor; });
--value;
cv.notify_all();
}
explicit Semaphore( T in = 0 ):value(std::forward<T>(in)) {}
private:
std::condition_variable cv;
mutable std::mutex m;
T value = 0;
auto lock() const; // see above
};
then
Semaphore workLimit(1);
void work() {
for(;;) {
workLimit.BoundedDecrement(0);
// work
workLimit.Increment();
std::this_thread::sleep_for(100ms);
}
}
void preventWork() {
workLimit.Decrement();
workLimit.WaitUntilExactValue(0);
}
void allowWork() {
workLimit.Increment();
}
Here, the workLimit is how many more workers are permitted to be working at this point. It is 1 to start with.
When a worker is working but not allowed to, it is -1. When it is working and allowed to, it is 0. When it is sleeping and allowed to work, it is 1. When it is sleeping (either because it is in sleep for, or bounded decrement) and not allowed to work, it is 0.
I am trying to create a data structure, ExpiringDeque. It should be somewhat similar to std::deque. Let's say I need only push_back(), size() and pop_front(). The data structure needs to automatically expire up to N first elements every T seconds.
This data structure needs to manage its own queue and expiration thread internally.
How do I write it in a thread safe way? This is an example that I came up with, does this seem reasonable? What am I missing?
#include <algorithm>
#include <atomic>
#include <cassert>
#include <deque>
#include <mutex>
#include <thread>
#include <unistd.h>
#include <iostream>
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}) {
expiringThread_ = std::thread{[&] () {
using namespace std::chrono_literals;
int waitCounter = 0;
while (true) {
if (!running_) {
return;
}
std::this_thread::sleep_for(1s);
if (waitCounter++ < interval_) {
continue;
}
std::lock_guard<std::mutex> guard(mutex_);
waitCounter = 0;
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
}};
}
~ExpiringDeque() {
running_ = false;
expiringThread_.join();
}
T pop_front() {
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
std::lock_guard<std::mutex> guard(mutex_);
T item = items_.front();
items_.pop_front();
return item;
}
int size() {
std::lock_guard<std::mutex> guard(mutex_);
return items_.size();
}
void push_back(T item) {
std::lock_guard<std::mutex> guard(mutex_);
items_.push_back(item);
}
private:
int numElements_;
int interval_;
std::atomic<bool> running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
};
int main() {
ExpiringDeque<int> ed(10, 3);
ed.push_back(1);
ed.push_back(2);
ed.push_back(3);
assert(ed.size() == 3);
assert(ed.pop_front() == 1);
assert(ed.size() == 2);
// wait for expiration
sleep(5);
assert(ed.size() == 0);
ed.push_back(10);
assert(ed.size() == 1);
assert(ed.pop_front() == 10);
return 0;
}
You can avoid an unnecessary wait in the destructor of ExpiringDeque by using a condition variable. I would also use std::condition_variable::wait_for with a predicate to check the running_ flag. This will ensure that you either wait for a timeout or a notification, whichever is earlier. You avoid using waitCounter and continue this way.
Another thing you should do is lock the mutex before checking the size of your deque in pop_front(), otherwise it's not thread safe.
Here's an updated version of your code:
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}), cv_() {
expiringThread_ = std::thread{ [&]() {
using namespace std::chrono_literals;
while (true) {
//Wait for timeout or notification
std::unique_lock<std::mutex> lk(mutex_);
cv_.wait_for(lk, interval_ * 1s, [&] { return !running_; });
if (!running_)
return;
//Mutex is locked already - no need to lock again
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
} };
}
~ExpiringDeque() {
//Set flag and notify worker thread
{
std::lock_guard<std::mutex> lk(mutex_);
running_ = false;
}
cv_.notify_one();
expiringThread_.join();
}
T pop_front() {
std::lock_guard<std::mutex> guard(mutex_);
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
T item = items_.front();
items_.pop_front();
return item;
}
...
private:
int numElements_;
int interval_;
bool running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
std::condition_variable cv_;
};
You can make the running_ flag a normal bool since the std::condition_variable::wait_for atomically checks for the timeout or notification.
I am trying to make a thread safe queue in C++17 based on condition variables.
How do I correctly interrupt the WaitAndPop() method in the queue's destructor?
The problem is that user classes will be waiting on the WaitAndPop() call to return before they destruct, meaning that their member queue never destructs, meaning that the return never happens, and I have a deadlock.
Here is a simplified example that illustrates the problem:
#include <condition_variable>
#include <future>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
using namespace std;
using namespace chrono_literals;
class ThreadsafeQueue {
private:
condition_variable cv_;
bool cancel_;
mutex mut_;
queue<int> queue_;
public:
ThreadsafeQueue() : cancel_(false){};
~ThreadsafeQueue() {
// although this would stop the cv, it never runs.
cancel_ = true;
cv_.notify_all();
scoped_lock<mutex> lk(mut_);
}
void Push(int x) {
{
scoped_lock<mutex> lk(mut_);
queue_.push(x);
}
cv_.notify_all();
}
// returns true if successful
bool WaitAndPop(int &out) {
unique_lock<mutex> lk(mut_);
cv_.wait(lk, [this]() { return cancel_ || ! queue_.empty(); });
if (cancel_) return false;
out = queue_.front();
queue_.pop();
return true;
}
};
class MyClass {
private:
future<void> fill_fut_;
future<void> serve_fut_;
ThreadsafeQueue queue_;
bool running_;
public:
MyClass() : running_(true) {
fill_fut_ = async(launch::async, &MyClass::FillThread, this);
serve_fut_ = async(launch::async, &MyClass::ServeThread, this);
};
~MyClass() {
running_ = false;
fill_fut_.get();
serve_fut_.get(); // this prevents the threadsafe queue from destructing,
// which
// prevents the serve thread from stopping.
}
void FillThread() {
while (running_) {
queue_.Push(rand() & 100);
this_thread::sleep_for(200ms);
}
}
void ServeThread() {
while (running_) {
int x;
bool ok = queue_.WaitAndPop(x); // this never returns because the queue
// never destructs
if (ok)
cout << "popped: " << x << endl; // prints five times
else
cout << "pop failed"; // does not reach here
}
}
};
int main() {
MyClass obj;
this_thread::sleep_for(1s);
return 0;
}
My little consumer-producer problem had me stumped for some time. I didn't want an implementation where one producer pushes some data round-robin to the consumers, filling up their queues of data respectively.
I wanted to have one producer, x consumers, but the producer waits with producing new data until a consumer is free again. In my example there are 3 consumers so the producer creates a maximum of 3 objects of data at any given time. Since I don't like polling, the consumers were supposed to notify the producer when they are done. Sounds simple, but the solution I found doesn't please me. First the code.
#include "stdafx.h"
#include <mutex>
#include <iostream>
#include <future>
#include <map>
#include <atomic>
std::atomic_int totalconsumed;
class producer {
using runningmap_t = std::map<int, std::pair<std::future<void>, bool>>;
// Secure the map of futures.
std::mutex mutex_;
runningmap_t running_;
// Used for finished notification
std::mutex waitermutex_;
std::condition_variable waiter_;
// The magic number to limit the producer.
std::atomic<int> count_;
bool can_run();
void clean();
// Fake a source, e.g. filesystem scan.
int fakeiter;
int next();
bool has_next() const;
public:
producer() : fakeiter(50) {}
void run();
void notify(int value);
void wait();
};
class consumer {
producer& producer_;
public:
consumer(producer& producer) : producer_(producer) {}
void run(int value) {
std::this_thread::sleep_for(std::chrono::milliseconds(42));
std::cout << "Consumed " << value << " on (" << std::this_thread::get_id() << ")" << std::endl;
totalconsumed++;
producer_.notify(value);
}
};
// Only if less than three threads are active, another gets to run.
bool producer::can_run() { return count_.load() < 3; }
// Verify if there's something to consume
bool producer::has_next() const { return 0 != fakeiter; }
// Produce the next value for consumption.
int producer::next() { return --fakeiter; }
// Remove the futures that have reported to be finished.
void producer::clean()
{
for (auto it = running_.begin(); it != running_.end(); ) {
if (it->second.second) {
it = running_.erase(it);
}
else {
++it;
}
}
}
// Runs the producer. Creates a new consumer for every produced value. Max 3 at a time.
void producer::run()
{
while (has_next()) {
if (can_run()) {
auto c = next();
count_++;
auto future = std::async(&consumer::run, consumer(*this), c);
std::unique_lock<std::mutex> lock(mutex_);
running_[c] = std::make_pair(std::move(future), false);
clean();
}
else {
std::unique_lock<std::mutex> lock(waitermutex_);
waiter_.wait(lock);
}
}
}
// Consumers diligently tell the producer that they are finished.
void producer::notify(int value)
{
count_--;
mutex_.lock();
running_[value].second = true;
mutex_.unlock();
std::unique_lock<std::mutex> waiterlock(waitermutex_);
waiter_.notify_all();
}
// Wait for all consumers to finish.
void producer::wait()
{
while (!running_.empty()) {
mutex_.lock();
clean();
mutex_.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// Looks like the application entry point.
int main()
{
producer p;
std::thread pthread(&producer::run, &p);
pthread.join();
p.wait();
std::cout << std::endl << std::endl << "Total consumed " << totalconsumed.load() << std::endl;
return 0;
}
The part I don't like is the list of values mapped to the futures, called running_. I need to keep the future around until the consumer is actually done. I can't remove the future from the map in the notify method or else I'll kill the thread that is currently calling notify.
Am I missing something that could simplify this construct?
template<class T>
struct slotted_data {
std::size_t I;
T t;
};
template<class T>
using sink = std::function<void(T)>;
template<class T, std::size_t N>
struct async_slots {
bool produce( slotted_data<T> data ) {
if (terminate || data.I>=N) return false;
{
auto l = lock();
if (slots[data.I]) return false;
slots[data.I] = std::move(data.t);
}
cv.notify_one();
return true;
}
// rare use of non-lambda cv.wait in the wild!
bool consume(sink<slotted_data<T>> f) {
auto l = lock();
while(!terminate) {
for (auto& slot:slots) {
if (slot) {
auto r = std::move(*slot);
slot = std::nullopt;
f({std::size_t(&slot-slots.data()), std::move(r)}); // invoke in lock
return true;
}
}
cv.wait(l);
}
return false;
}
// easier and safer version:
std::optional<slotted_data<T>> consume() {
std::optional<slotted_data<T>> r;
bool worked = consume([&](auto&& data) { r = std::move(data); });
if (!worked) return {};
return r;
}
void finish() {
{
auto l = lock();
terminate = true;
}
cv.notify_all();
}
private:
auto lock() { return std::unique_lock<std::mutex>(m); }
std::mutex m;
std::condition_variable cv;
std::array< std::optional<T>, N > slots;
bool terminate = false;
};
async_slots provides a fixed number of slots and an awaitable consume. If you try to produce two things in the same slot, the producer function returns false and ignores you.
consume invokes the sink of the data inside the mutex in a continuation passing style. This permits atomic consumption.
We want to invert producer and consumer:
template<class T, std::size_t N>
struct slotted_consumer {
bool consume( std::size_t I, sink<T> sink ) {
std::optional<T> data;
std::condition_variable cv;
std::mutex m;
bool worked = slots.produce(
{
I,
[&](auto&& t){
{
std::unique_lock<std::mutex> l(m);
data.emplace(std::move(t));
}
cv.notify_one();
}
}
);
if (!worked) return false;
std::unique_lock<std::mutex> l(m);
cv.wait(l, [&]()->bool{
return (bool)data;
});
sink( std::move(*data) );
return true;
}
bool produce( T t ) {
return slots.consume(
[&](auto&& f) {
f.t( std::move(t) );
}
);
}
void finish() {
slots.finish();
}
private:
async_slots< sink<T>, N > slots;
};
we have to take some care to execute sink in a context where we are not holding the mutex of async_slots, which is why consume above is so strange.
Live example.
You share a slotted_consumer< int, 3 > slots. The producing thread repeatedly calls slots.produce(42);. It blocks until a new consumer lines up.
Consumer #2 calls slots.consume( 2, [&](int x){ /* code to consume x */ } ), and #1 and #0 pass their slot numbers as well.
All 3 consumers can be waiting for the next production. The above system defaults to feeding #0 first if it is waiting for more work; we could make it "fair" at a cost of keeping a bit more state.
Having several threads running I need to guaranty that every of my threads reached a certain point before proceeding. I need to implement a kind of barrier. Consider a function func which can be run from several threads:
void func()
{
operation1();
// wait till all threads reached this point
operation2();
}
What is best way to realise this barrier using C++ 11 and VS12, considering boost if needed.
You could use boost::barrier
Unfortunately, the thread barrier concept itself is not part of c++11 or visual c++.
In pure c++11 you could use a condition variable and a counter.
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
class my_barrier
{
public:
my_barrier(int count)
: thread_count(count)
, counter(0)
, waiting(0)
{}
void wait()
{
//fence mechanism
std::unique_lock<std::mutex> lk(m);
++counter;
++waiting;
cv.wait(lk, [&]{return counter >= thread_count;});
cv.notify_one();
--waiting;
if(waiting == 0)
{
//reset barrier
counter = 0;
}
lk.unlock();
}
private:
std::mutex m;
std::condition_variable cv;
int counter;
int waiting;
int thread_count;
};
int thread_waiting = 3;
my_barrier barrier(3);
void func1()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
barrier.wait();
std::cout << "I have awakened" << std::endl;
}
void func2()
{
barrier.wait();
std::cout << "He has awakened!!" << std::endl;
}
int main() {
std::thread t1(func1);
std::thread t2(func2);
std::thread t3(func2);
t1.join();
t2.join();
t3.join();
}
Each thread wait till a predicate is met. The last thread will make the predicate valid, and allow the waiting threads to proceed. If you want to reuse
the barrier (for instance call the function multiple times), you need another
variable to reset the counter.
This current implementation is limited. A calling func();func(); twice may not make threads wait the second time.
An option could be the use of OpenMP framework.
#include <omp.h>
void func()
{
#pragma omp parallel num_threads(number_of_threads)
{
operation1();
#pragma omp barrier
// wait till all threads reached this point
operation2();
}
}
Compile the code with -fopenmp
Solution:
#include <cassert>
#include <condition_variable>
class Barrier
{
public:
Barrier(std::size_t nb_threads)
: m_mutex(),
m_condition(),
m_nb_threads(nb_threads)
{
assert(0u != m_nb_threads);
}
Barrier(const Barrier& barrier) = delete;
Barrier(Barrier&& barrier) = delete;
~Barrier() noexcept
{
assert(0u == m_nb_threads);
}
Barrier& operator=(const Barrier& barrier) = delete;
Barrier& operator=(Barrier&& barrier) = delete;
void Wait()
{
std::unique_lock< std::mutex > lock(m_mutex);
assert(0u != m_nb_threads);
if (0u == --m_nb_threads)
{
m_condition.notify_all();
}
else
{
m_condition.wait(lock, [this]() { return 0u == m_nb_threads; });
}
}
private:
std::mutex m_mutex;
std::condition_variable m_condition;
std::size_t m_nb_threads;
};
Example:
#include <chrono>
#include <iostream>
#include <thread>
Barrier barrier(2u);
void func1()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
barrier.Wait();
std::cout << "t1 awakened" << std::endl;
}
void func2()
{
barrier.Wait();
std::cout << "t2 awakened" << std::endl;
}
int main()
{
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
return 0;
}
Try It Online: WandBox