Why doesn't the following barrier implementation work:
void JoinQuery::barrier() {
std::unique_lock<std::mutex> lk(barrier_mutex);
barrier_count ;
if (barrier_count == NUM_THREADS) {
barrier_count = 0;
lk.unlock();
barrier_cv.notify_all();
} else {
barrier_cv.wait(lk, [this] { return barrier_count == 0; });
}
}
However it works if I use a second variable to track the condition of all threads arriving.
But I don't know why this is necessary. It should be impossible for a thread to see barrier_count == 0 before all threads have arrived.
CodePudding user response:
Is this supposed to be a one-time barrier? Where once N threads have hit the barrier, all further calls are non blocking? In that case, you would want a bool flag indicating that has tripped, and check that as well.
The problem is that if barrier()
is called more than NUM_THREAD
times, it will increase barrier_count
again, resulting in any waiting threads going back to sleep.
CodePudding user response:
It works well with 1000 threads
#include <vector>
#include <thread>
#include <condition_variable>
using namespace std;
int NUM_THREADS = 1000;
class JoinQuery {
mutex barrier_mutex;
condition_variable barrier_cv;
int barrier_count = 0;
public:
void barrier(int num) {
std::unique_lock<std::mutex> lk(barrier_mutex);
barrier_count ;
if (barrier_count == NUM_THREADS) {
barrier_count = 0;
barrier_cv.notify_all();
} else {
barrier_cv.wait(lk, [this] { return barrier_count == 0; });
printf("wake num=%d\n", num);
}
}
};
int main(){
JoinQuery jq;
vector<thread> ts;
for (int i = 0; i < NUM_THREADS; i ) {
ts.push_back(thread(&JoinQuery::barrier, ref(jq), i));
}
for (auto& t : ts) {
t.join();
}
}