Creating a Blocking Queue
有时这种
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
#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> void push(T value) { bool empty() { void fillWorkQueue(BlockingQueue<string>& workQueue) { void doWork(BlockingQueue<string>& workQueue) { void multiThreaded() { int main() { // Multi Threaded |
请看这里:
我从空 std 容器的 front() 中得到什么?
如果你在一个空容器上调用
试试:
1
2 3 4 5 6 7 8 9 10 11 12 |
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:
1
|
bool pop(T &t); // returns false if there was nothing to read.
|
更好的是,如果您确实在重要的地方使用它,您可能希望在删除之前标记正在使用的项目以防失败。
1
2 3 |
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) |
问题应该出在这里:
1
2 3 |
while(!itemQueue.empty()) {
itemQueue.pop(); } |
您在检查剩余值时保留互斥锁,然后释放互斥锁,可能会发生另一个线程执行,发现剩余值并将其弹出。在最坏的情况下,之后不会留下任何项目,并且第一个线程会尝试在没有留下任何元素的情况下弹出。
解决方案是在同一节中的内部队列上进行前/弹出调用,而不是在同一锁定节中检查空,然后将始终定义行为。
另一个建议是在使用互斥锁时使用
考虑到这两个建议,您的
1
2 3 4 5 6 7 8 9 10 |
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 |
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/268846.html