我正在寻找有关我正在使用的c ++设计问题的一些建议。关于这个问题的一些背景......
我有如下所示的Runnable类:
class Runnable
{
public:
Runnable();
virtual ~Runnable();
void Stop();
void Start();
Runnable(Runnable const&) = delete;
Runnable& operator =(Runnable const&) = delete;
protected:
virtual void Run() = 0;
// main thread function.
std::atomic<bool> mStop;
private:
static void StaticRun(void *);
std::thread mThread;
};
然后我有一个ExpirationMap继承Runnable类,如下所示:
class ExpirationMap : Runnable
{
public:
explicit ExpirationMap();
virtual ~ExpirationMap();
void Init(uint8_t);
void Run() override;
virtual void DoExpire(uint8_t) = 0;
// Expiry function to be implemented by the derived classes.
private:
uint8_t mDelay;
};
我有一个继承ExpirationMap类的第三个类。该类封装了std :: unorderd_map。
模板
class MyMap : public ExpirationMap
{
public:
void DoExpire(uint8_t) override;
void Init(uint8_t);
void Add(const KeyType, const ValueType&);
ValueType Get(const KeyType);
bool Exists(const KeyType);
ValueType Remove(const KeyType);
void Clear();
...
private:
std::unordered_map<KeyType, ValueType> mMap;
std::shared_ptr<boost::shared_mutex> mLock;
};
MyMap :: Init启动ExpirationMap :: Init,它以MyMap :: DoExpire作为线程函数生成一个线程。 MyMap :: DoExpire基本上是一个永无止境的while循环。该线程的基本工作是扫描MyMap的元素并删除过期的条目。映射的每个元素(值)都有一个到期时间,用于检查元素是否是到期的候选者。所有这些都已实施并且运作良好。
对于长篇介绍感到抱歉,但现在还有真正的问题。 现在,我有一种情况,我必须将此代码移植到基于事件循环的平台。由于事件循环系统支持带回调的定时器,我可以将DoExpire函数作为回调函数传递给定时器函数。但是,我试图看看是否有更好的方法来重构代码,以便代码在平台上工作,即基于线程(我现在拥有)和基于事件循环,同时最小化重复。在创建MyMap时,我希望能够说:创建一个使用基于线程的到期或基于计时器+回调的到期的地图。非常感谢任何建议或意见。感谢。
答案 0 :(得分:0)
我认为你可以比任何一种方法做得更好 - 你可以做到这一点,这样你根本不需要定期做任何事情,因此你不需要事件循环或更新线程。
由于地图中的每个条目都有与之关联的到期时间,因此您需要做的就是在地图对象周围构建一个API层,假装过期的对象不再存在,例如: (伪代码):
bool ExpirationMap :: Exists(const KeyType & key) const
{
if (mMap.has_key(key) == false) return false;
return (mMap[key].mExpirationTime < now); // expired entries don't count!
}
ValueType ExpirationMap :: Get(const KeyType & key) const
{
return Exists(key) ? mMap[key] : ValueType();
}
这足以获得您想要的行为;唯一剩下的问题(根据您的使用情况可能会或可能不是实际问题)是地图可能会随着时间的推移变大,充满无用的旧/过期条目。这可以通过各种方式处理(包括忽略问题,如果内存使用不是问题,或者仅在查找并发现它已过期时删除条目),而是一种接近最佳的方式处理它将保留第二个内部数据结构(例如std :: priority_queue,它保存按到期时间排序的条目);然后,无论何时调用任何方法,您都可以执行以下操作:
while(mEntriesByExpirationTime.size() > 0)
{
const ByTimeEntry & firstEntry = mEntriesByExpirationTime.begin();
if (firstEntry.mExpirationTime < now)
{
mMap.erase(firstEntry.mKey);
mEntriesByExpirationTime.pop();
}
else break;
}
...由于此priority_queue中的条目以到期时间顺序保存,因此该调用尽可能便宜,因为它永远不会迭代不仅仅是应该立即删除的过期条目。 / p>
一种不需要程序定期唤醒的设计通常优于一种设计,特别是在笔记本电脑和手机等功耗受限的平台上。如果您的程序经常要求被唤醒,则CPU无法有效地睡眠:)