在c++队列中使用pop和visit实现线程安全

thread safe with pop and visit in c++ queue

本文关键字:visit 实现 线程 安全 pop c++ 队列      更新时间:2023-10-16

我知道std::queue不是线程安全的,但我不想锁定队列。所以我用pop&限制使用。

例如,当我想弹出:我有一个枚举来表示第一个元素状态

enum {
Busy = 1,
Unused,
}

当我向队列添加elment时:

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();
}