问题描述
我正在尝试创建一个数据结构 ExpiringDeque。它应该有点类似于 std::deque 。假设我只需要 push_back()、size() 和 pop_front()。数据结构需要每 T 秒自动过期最多 N 个第一个元素。
这个数据结构需要内部管理自己的队列和过期线程。 如何以线程安全的方式编写它?这是我想出来的一个例子,这看起来合理吗?我错过了什么?
img = cv2.cvtColor(img,cv2.COLOR_GRAY2BGB)
解决方法
您可以通过使用条件变量避免在 ExpiringDeque
的析构函数中进行不必要的等待。我还会使用带有谓词的 std::condition_variable::wait_for
来检查 running_
标志。这将确保您等待超时或通知,以较早者为准。您避免以这种方式使用 waitCounter
和 continue
。
您应该做的另一件事是在检查 pop_front()
中双端队列的大小之前锁定互斥锁,否则它不是线程安全的。
这是您的代码的更新版本:
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n,int t) : numElements_(n),interval_(t),running_(true),items_({}),cv_() {
expiringThread_ = std::thread{ [&]() {
using namespace std::chrono_literals;
while (true) {
//Wait for timeout or notification
std::unique_lock<std::mutex> lk(mutex_);
cv_.wait_for(lk,interval_ * 1s,[&] { return !running_; });
if (!running_)
return;
//Mutex is locked already - no need to lock again
int numToErase = std::min(numElements_,static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(),items_.begin() + numToErase);
}
} };
}
~ExpiringDeque() {
//Set flag and notify worker thread
{
std::lock_guard<std::mutex> lk(mutex_);
running_ = false;
}
cv_.notify_one();
expiringThread_.join();
}
T pop_front() {
std::lock_guard<std::mutex> guard(mutex_);
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
T item = items_.front();
items_.pop_front();
return item;
}
...
private:
int numElements_;
int interval_;
bool running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
std::condition_variable cv_;
};
您可以将 running_
标志设为普通 bool
,因为 std::condition_variable::wait_for
会自动检查超时或通知。