有时BlockingQueue
的实现和执行才有效。有时它会出现段错误。知道为什么吗?
#include <thread>
using std::thread;
#include <mutex>
using std::mutex;
#include <iostream>
using std::cout;
using std::endl;
#include <queue>
using std::queue;
#include <string>
using std::string;
using std::to_string;
#include <functional>
using std::ref;
template <typename T>
class BlockingQueue {
private:
mutex mutex_;
queue<T> queue_;
public:
T pop() {
this->mutex_.lock();
T value = this->queue_.front();
this->queue_.pop();
this->mutex_.unlock();
return value;
}
void push(T value) {
this->mutex_.lock();
this->queue_.push(value);
this->mutex_.unlock();
}
bool empty() {
this->mutex_.lock();
bool check = this->queue_.empty();
this->mutex_.unlock();
return check;
}
};
void fillWorkQueue(BlockingQueue<string>& workQueue) {
int size = 40000;
for(int i = 0; i < size; i++)
workQueue.push(to_string(i));
}
void doWork(BlockingQueue<string>& workQueue) {
while(!workQueue.empty()) {
workQueue.pop();
}
}
void multiThreaded() {
BlockingQueue<string> workQueue;
fillWorkQueue(workQueue);
thread t1(doWork, ref(workQueue));
thread t2(doWork, ref(workQueue));
t1.join();
t2.join();
cout << "done\n";
}
int main() {
cout << endl;
// Multi Threaded
cout << "multiThreaded\n";
multiThreaded();
cout << endl;
}
答案 0 :(得分:4)
问题应放在这里:
while(!itemQueue.empty()) {
itemQueue.pop();
}
在检查剩余值时保留互斥锁,然后释放互斥锁,可能会发生另一个线程被执行,发现剩下一个值并弹出它。在最坏的情况下,之后没有剩下任何项目,并且第一个线程尝试弹出而没有剩余元素。
解决方案是在同一部分中对内部队列进行前/弹出调用,而不是在同一个锁定部分中检查为空,然后始终定义行为。
另一个建议是在使用互斥锁时使用std::lock_guard
因为它提高了可读性,并确保无论发生什么情况都会释放互斥锁。
考虑到这两个建议的事实,您的pop
方法可能如下:
T pop() {
std::lock_guard lock(this->mutex_); //mutex_ is locked
T value;
if( !this->queue_.empty() )
{
value = this->queue_.front();
this->queue_.pop();
}
return value;
} //mutex_ is freed
答案 1 :(得分:3)
见这里:
What do I get from front() of empty std container?
如果您在空容器上调用.front()
,则会发生错误,最好先检查.empty()
。
尝试:
T pop() {
this->mutex_.lock();
T value;
if( !this->queue_.empty() )
{
value = this->queue_.front(); // undefined behavior if queue_ is empty
// may segfault, may throw, etc.
this->queue_.pop();
}
this->mutex_.unlock();
return value;
}
注意:由于原子操作在这种队列中很重要,我建议更改API:
bool pop(T &t); // returns false if there was nothing to read.
更好的是,如果你真的在重要的地方使用它,你可能想要在删除之前标记正在使用的项目以防万一。
bool peekAndMark(T &t); // allows one "marked" item per thread
void deleteMarked(); // if an item is marked correctly, pops it.
void unmark(); // abandons the mark. (rollback)