C++设计一个具有过期/持续时间的容器
C++ designing a container with expiration/duration
我需要提供一个小的关联数组(比如TimedMap),其中键和值的类型是不确定的。这个关联数组的所有元素都有一个固定的持续时间。例如,假设键类型为"int",值类型为"Foo",持续时间为5秒。如果我插入(0,Foo实例),那么在5秒钟后,该值应该会消失。
由于我的同事将使用这个关联数组,他不想为数组的获取/设置操作锁定/解锁,并且假设我别无选择。除了boost::shared_ptr之外,由于某些原因,我不允许使用boost。
TimedMap实例应该只在少数地方使用,所以我决定为每个实例创建一个线程,然后定期检查所有元素,以便在密钥、值对过期时删除它。
TimedMap的用户将这样使用:
typedef TimedMap<int, boost::shared_ptr<Foo> > TMAP;
TMAP m(5); // each key, value pair will have 5 seconds lifetime.
m.set(0, boost::shared_ptr<Foo>(new Foo()));
{
TMAP::Ptr ptr = m.get(0); // index 0, and value is locked at this time
if (ptr) { // if it was in the map,
// use (*ptr).xxx() to access Foo::xxx.
}
} // now, 'ptr' is gone, so the lock will be gone.
现在,除了线程的事情,我想我实现了其余的。有两种类型的锁,每个TimedMap实例一个锁,每个元素将有一个锁用于保护每个元素。
问题是,有时,当调用TimedMap实例析构函数时,主锁由于未知原因处于锁定状态。我查了一会儿,什么也没发现。
我知道在标准容器的元素中放置锁,但目前我想不出更好的解决方案。
你能帮我做这个吗?为了编译它,
$ g++ timedmap.cpp
根据我的说法,我无法缩短出处。
timedmap.h:
#include <map>
#include <stdexcept>
#include <string>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#define M_LOCK(m) ((m).lock())
#define M_UNLOCK(m) ((m).unlock())
#define M_TRYLOCK(m) ((m).trylock())
template <typename K, typename V>
class TimedMap {
class MUTEX {
::pthread_mutex_t lck_;
public:
MUTEX(const MUTEX &m) {
// copy CTOR
int type = PTHREAD_MUTEX_DEFAULT;
::pthread_mutexattr_t attr;
::pthread_mutexattr_init(&attr);
::pthread_mutexattr_settype(&attr, type);
::pthread_mutex_init(&lck_, &attr);
::pthread_mutexattr_destroy(&attr);
}
MUTEX(int type = PTHREAD_MUTEX_DEFAULT) {
::pthread_mutexattr_t attr;
::pthread_mutexattr_init(&attr);
::pthread_mutexattr_settype(&attr, type);
::pthread_mutex_init(&lck_, &attr);
::pthread_mutexattr_destroy(&attr);
}
~MUTEX() {
int ret = ::pthread_mutex_destroy(&lck_);
if (ret) {
// pthread_mutex_destroy() failed
if (ret == EBUSY) {
// locked?
}
//abort();
}
}
int lock() {
int ret = pthread_mutex_lock(&lck_);
if (ret) { // lock failed
abort();
}
return ret;
}
int unlock() {
int ret = pthread_mutex_unlock(&lck_);
if (ret) { // unlock failed
abort();
}
return ret;
}
bool trylock() {
int ret = pthread_mutex_trylock(&lck_);
if (ret != 0 && ret != EBUSY) { // trylock failed
abort();
}
return ret ? false : true;
}
};
struct TMENT {
time_t tm_;
MUTEX m_;
V val_;
TMENT() : tm_(time(0)), m_(), val_() {}
TMENT(const V &val) : tm_(time(0)), m_(), val_(val) {
}
void refresh() { tm_ = time(0); }
bool expired(time_t expiration) {
time_t now = ::time(0);
return (now - tm_) > expiration;
}
};
typedef std::map<K, TMENT> map_type;
typedef typename map_type::key_type key_type;
typedef typename map_type::mapped_type mapped_type;
typedef typename map_type::value_type value_type;
TimedMap(const TimedMap &);
map_type map_;
MUTEX mlock_;
int expiration_;
bool refresh_;
public:
explicit TimedMap(int expiration)
: map_(), mlock_(PTHREAD_MUTEX_ERRORCHECK),
expiration_(expiration), refresh_(false) {
}
~TimedMap() {
clear();
}
void clear() {
M_LOCK(mlock_);
typename map_type::iterator i = map_.begin();
while (i != map_.end()) {
TMENT &ent = (*i).second;
M_LOCK(ent.m_);
M_UNLOCK(ent.m_);
map_.erase(i++);
}
M_UNLOCK(mlock_);
}
class Ptr {
TMENT *ent_;
mutable bool owned_;
bool yield() const {
if (!owned_) {
// Ptr can't yield ownership when it doesn't");
abort();
}
owned_ = false;
return true;
}
Ptr &operator=(const Ptr &ptr);
public:
Ptr(const Ptr &ptr) : ent_(ptr.ent_) {
owned_ = ptr.yield();
}
Ptr() : ent_(0), owned_(false) {}
Ptr(TMENT *ent) : ent_(ent), owned_(true) {}
~Ptr() {
if (owned_)
M_UNLOCK(ent_->m_);
}
V *operator->() {
if (!owned_)
throw std::out_of_range("not found");
return &(ent_->val_);
}
V &operator*() {
if (!owned_)
throw std::out_of_range("not found");
return ent_->val_;
}
operator bool() {
if (owned_)
return true;
return false;
}
};
void set(const key_type &k, const V &v) {
M_LOCK(mlock_);
typename map_type::iterator i = map_.find(k);
if (i == map_.end()) {
map_[k] = TMENT(v);
}
else {
TMENT &ent = (*i).second;
M_LOCK(ent.m_);
ent.refresh();
ent.val_ = v;
M_UNLOCK(ent.m_);
}
M_UNLOCK(mlock_);
}
Ptr get(const key_type &k) {
M_LOCK(mlock_);
typename map_type::iterator i = map_.find(k);
if (i == map_.end()) {
// key not found
M_UNLOCK(mlock_);
return Ptr();
}
TMENT &ent = (*i).second;
M_LOCK(ent.m_);
if (ent.expired(expiration_)) {
// key expired
M_UNLOCK(ent.m_);
return Ptr();
}
else if (refresh_)
ent.refresh();
M_UNLOCK(mlock_);
// Intentionally leave the 'ent' as locked state.
return Ptr(&ent);
}
};
timedmap.pp:
#include <cstdio>
#include <boost/shared_ptr.hpp>
#include <time.h>
#include "timedmap.h"
class Foo {
public:
Foo() {
}
~Foo() {
}
const char *what() {
return "foo";
}
};
typedef TimedMap<int, boost::shared_ptr<Foo> > TMAP;
int
main(void)
{
{
TMAP m(5);
int key = 0;
{
m.set(key, boost::shared_ptr<Foo>(new Foo()));
}
bool loop = true;
{
while (loop) {
{
TMAP::Ptr ptr = m.get(key);
if (ptr) {
fprintf(stderr, "main: key(%d) = %sn", key, (*ptr)->what());
}
else {
fprintf(stderr, "main: key(%d) was not theren", key);
break;
}
}
//break;
usleep(100000);
//sleep(1);
}
}
m.clear();
}
return 0;
}
我建议有一个第三个字段,它是元素的寿命。
您需要一个friend
方法,该方法在每次解析时都会唤醒并缩短生存时间。当使用寿命为零时,元素将被移除。
您将需要一个单独的执行线程来处理生命周期,因为您不想挂起(阻塞)客户端的线程。
该实现是特定于操作系统的,或者涉及特定于操作操作系统的功能。
此外,使用互斥或其他机制来防止客户端线程在生命周期进程处于活动状态时访问元素。
编辑1:
由于life-time任务可以修改容器,因此应将包含标记为volatile
。
- 从持续时间构造std::chrono::system_clock::time_point
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 时间持续时间到时间字符串
- 指向(数据)成员的指针作为非类型模板参数,例如具有自动存储持续时间/无链接
- 为什么具有静态存储持续时间的同一内联变量在包含在 VS2017 编译的两个翻译单元中时会构造和销毁两次
- 将毫秒转换为给定格式的持续时间
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- 划分和乘以STD :: Chrono ::持续时间
- 静态存储持续时间初始化
- C++计划持续时间内(字体)资源的分配
- FFMPEG:具有不同持续时间的多路复用流
- 在不同翻译单元中具有静态存储持续时间的依赖非局部常量浮点变量的常量初始化
- 使用System_Clock :: TO_TIME_T警告持续时间_t
- 访问和存储/解析性std :: Chrono ::持续时间:: milliseconds(cpprest)时使用什么类型
- 我可以让QT到概要文件插槽执行持续时间吗?
- 在STD :: Chrono ::剩余时间测量的持续时间
- 自定义 AVIOContext 的未定义 AVFormatContext 持续时间
- 使用计时比较C++的持续时间/秒数并不像预期的那样工作
- 为什么 std::future::wait_for 不等待正确的持续时间
- C++设计一个具有过期/持续时间的容器