Reputation: 81
I know std::queue is not thread safe, but I don't want to lock queue. so I use pop & push limit on usages.
for instance, when I want to pop: I have an enum to express first element status
enum {
Busy = 1,
Unused,
}
when I add an elment to queue:
void UserAdd() {
lock.lock();
element.status = BUSY;
queue.push_back(element);
lock.unlock();
}
when I visit :
//only visit function, and every element only called once.
void UserVisit() {
auto header = queue.front();
.......
queue.front().status = UNUSED;
return ;
}
I judge first elements status when I want to pop element.
If first element is Busy, wait;
If first element is Unused, pop;
void UserPop() {
while (queue.front().status != Unused) {
usleep(200);
}
lock.lock();
queue.pop();
lock.unlock();
}
thread A: 1. UserAdd, 2. UserVisit, 1.UserAdd, 2.UserVisit loop ...
thread B: 1. UserPop.
Is UserPop() && UserVisit thread safe ?.
I think It's thread safe.
Upvotes: 3
Views: 1289
Reputation: 24758
Note that the member function pop()
does modify the queue. If UserPop()
is called by multiple threads concurrently, one thread may modify the queue by calling pop()
on it at the same time another thread is reading the queue by calling front()
:
void UserPop() {
while (queue.front().status != Unused) { // <-- read queue
usleep(200);
}
queue.pop(); // <-- modify queue
}
Since the queue itself, std::queue
, doesn't handle concurrent access for you, UserPop()
is not thread-safe.
A simple approach to make it thread-safe is to add a mutex and hold a lock on it when reading or modifying the queue:
std::mutex mtx;
// ...
void UserPop() {
std::unique_lock<std::mutex> lck(mtx);
// mtx is locked at this point
while (queue.front().status != Unused) {
lck.unlock();
usleep(200); // mtx is not locked
lck.lock();
}
// mtx is locked at this point
queue.pop();
}
std::queue
's member functions front()
and pop()
above are always called while holding a lock on the mutex.
However, you may want to consider using std::condition_variable
instead. It provides an optimization over the busy waiting:
std::mutex mtx;
std::condition_variable cv;
void UserPop() {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, [this]() { return queue.front().status == Unused; });
queue.pop();
}
Upvotes: 3