我想要做的是将整数推送到我的threadSafe队列实现与多个线程,并与另一系列的线程同时弹出插入的数字。所有这些操作都必须是线程安全的,但我想要的另一个选项是必须修复队列的大小,就像缓冲区一样。如果缓冲区已满,则所有推送线程必须等待弹出线程释放一些插槽。
这是我对队列/缓冲区的实现,它似乎可以工作,但经过几次迭代后它就会停止并保持阻塞而没有任何错误。
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
template <typename T>
class Queue
{
private:
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable cond_;
public:
T pop()
{
std::unique_lock<std::mutex> mlock(mutex_);
cond_.wait(mlock, [this]{return !queue_.empty();});
auto val = queue_.front();
queue_.pop();
return val;
}
void pop(T& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
cond_.wait(mlock, [this]{return !queue_.empty();});
item = queue_.front();
queue_.pop();
}
void push(const T& item, int buffer)
{
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.size() >= buffer)
{
cond_.wait(mlock);
}
queue_.push(item);
mlock.unlock();
cond_.notify_one();
}
Queue()=default;
Queue(const Queue&) = delete; // disable copying
Queue& operator=(const Queue&) = delete; // disable assignment
};
缓冲区的大小在push函数中使用变量缓冲区定义。这是一个使用示例:
void prepare(Queue<int>& loaded, int buffer, int num_frames)
{
for (int i = 0; i < num_frames; i++)
{
cout<< "push "<<i<<endl;
loaded.push(i, buffer);
}
}
void load (vector<Frame>& movie, Queue<int>& loaded, int num_frames,
int num_points, int buffer, int height, int width)
{
for (int i = 0; i < num_frames; i++)
{
int num = loaded.pop();
cout<< "pop "<<num<<endl;
}
}
int main()
{
srand(time(NULL));
int num_threadsXstage = 4;
int width = 500;
int height = 500;
int num_points = width * height;
int num_frames = 100;
int frames_thread = num_frames/num_threadsXstage;
int preset = 3;
int buffer = 10;
//Vectors of threads
vector<thread> loader;
//Final vector
vector<Frame> movie;
movie.resize(num_frames);
//Working queues
Queue<int> loaded;
//Prepare loading queue task
thread preparator(prepare, ref(loaded), buffer, num_frames);
for (int i = 0; i < num_threadsXstage; i++)
{
//stage 1
loader.push_back(thread(&load, ref(movie), ref(loaded), frames_thread,
num_points, buffer, height, width));
}
// JOIN
preparator.join();
join_all(loader);
return 0;
}
答案 0 :(得分:2)
您的pop
函数可能允许线程等待push
前进,但它们不会调用任何notify
函数。只要可以使条件变量上阻塞的线程成为可能,就必须调用相应的notify
函数。
虽然解释原因非常复杂,但您应该在保持锁定的同时拨打notify_all
或致电notify_one
。理论上可以“唤醒错误的线程”,因为您对两个谓词使用相同的条件变量(队列不为空且队列未满)。
为了避免很难理解故障模式,请始终执行以下三项操作之一:
notify_all
,绝不使用notify_one
;或只要遵循这三条规则中的至少一条,就可以避免一种模糊的故障模式,在这种模式下,只释放一个在释放互斥锁后选择休眠的线程,同时保留唯一可以处理该条件的线程仍然被阻塞