我需要提供一个小的关联数组(比如TimedMap),其中key和value类型没有决定。该关联数组对其所有元素具有固定的持续时间。例如,假设键类型为'int',值类型为'Foo',持续时间为5秒。如果我插入(0,Foo实例),那么在5秒后,该值应该消失。
由于我的同事将使用这个关联数组,不想为数组的get / set操作锁定/解锁,并且假设我别无选择。除了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.cpp:
#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) = %s\n", key, (*ptr)->what());
}
else {
fprintf(stderr, "main: key(%d) was not there\n", key);
break;
}
}
//break;
usleep(100000);
//sleep(1);
}
}
m.clear();
}
return 0;
}
答案 0 :(得分:0)
我建议使用第3个字段,即元素的生命周期。
您需要使用friend
方法唤醒每个解决时间并减少生命周期。当寿命达到零时,元素被移除。
您需要一个单独的执行线程来处理生命周期,因为您不想暂停(阻止)客户端的线程。
实施是特定于操作系统的,或涉及操作系统特定的功能。
此外,在生命周期过程处于活动状态时,使用互斥锁或其他机制来阻止客户端线程访问元素。
编辑1:
由于容器可以通过生命周期任务进行修改,因此包含应标记为volatile
。