我知道 std::queue 不是线程安全的,但我不想锁定队列。 所以我对使用情况使用弹出和推送限制。
例如, 当我想弹出时: 我有一个枚举来表示第一个元素状态
enum {
Busy = 1,
Unused,
}
当我将元素添加到队列时:
void UserAdd() {
lock.lock();
element.status = BUSY;
queue.push_back(element);
lock.unlock();
}
当我访问时:
//only visit function, and every element only called once.
void UserVisit() {
auto header = queue.front();
.......
queue.front().status = UNUSED;
return ;
}
当我想要弹出元素时,我判断第一个元素的状态。
如果第一个元素为 Busy,则等待;
如果第一个元素未使用,则弹出;
void UserPop() {
while (queue.front().status != Unused) {
usleep(200);
}
lock.lock();
queue.pop();
lock.unlock();
}
线程A:1.UserAdd,2.UserVisit,1.UserAdd,2.UserVisit循环...
线程B:1.UserPop。
UserPop() && UserVisit 线程安全吗?
我认为它是线程安全的。
最佳答案
没有线程安全
请注意,成员函数 pop()
确实会修改队列。如果 UserPop()
被多个线程同时调用,则一个线程可以通过调用 pop()
来修改队列,同时另一个线程通过调用来读取队列front()
:
void UserPop() {
while (queue.front().status != Unused) { // <-- read queue
usleep(200);
}
queue.pop(); // <-- modify queue
}
由于队列本身 std::queue
不为您处理并发访问,因此 UserPop()
不是线程安全的。
使其线程安全
使其成为线程安全的一个简单方法是添加互斥体并在读取或修改队列时对其持有锁:
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
的成员函数 front()
和 pop()
总是在持有互斥锁时被调用.
但是,您可能需要考虑使用 std::condition_variable
反而。它提供了对繁忙等待的优化:
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();
}
关于c++ - C++ 队列中的 pop 和访问是线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60262705/