c++ 过期映射条目线程与事件循环



我正在寻找一些关于我遇到的c ++设计问题的建议。关于这个问题的一些背景...

我有可运行的类,如下所示:

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;
};

然后我有一个继承 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;
};

我有第三个类继承了 ExexpationMap 类。此类封装 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 启动了 ExexpationMap::Init,它生成了一个使用 MyMap::D oExpire 作为线程函数的线程。MyMap::D oExpire基本上是一个永无止境的循环。线程的基本工作是扫描 MyMap 的元素并删除过期的条目。映射的每个元素(值)都有一个过期时间,用于检查元素是否是到期的候选元素。所有这些都已实施并运行良好。

很抱歉介绍很长,但现在进入真正的问题。 现在,我遇到一种情况,即我必须将此代码移植到基于事件循环的平台。由于事件循环系统支持带回调的计时器,我可以传入 DoExpire 函数作为计时器函数的回调。但是,我正在尝试查看是否有更好的方法来重构代码,以便代码在两个平台上都有效,即基于线程(我现在拥有的)和基于事件循环,同时最大限度地减少重复。在创建 MyMap 时,我希望能够说:创建一个使用基于线程的到期或基于计时器+回调的到期的映射。任何建议或建议将不胜感激。谢谢。

我认为你可以比任何一种方法做得更好——你可以做到这一点,这样你根本不需要定期做任何事情,因此你不需要事件循环或更新线程。

由于映射中的每个条目都已经有一个与之关联的过期时间,因此您需要做的就是围绕映射对象构建一个 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::p riority_queue 保存按过期时间排序的条目);然后,每当调用任何方法时,您都可以执行以下操作:

while(mEntriesByExpirationTime.size() > 0)
{
const ByTimeEntry & firstEntry = mEntriesByExpirationTime.begin();
if (firstEntry.mExpirationTime < now)
{
mMap.erase(firstEntry.mKey);
mEntriesByExpirationTime.pop();
}
else break;
}

。由于此priority_queue中的条目按过期时间顺序保存,因此此调用尽可能便宜,因为它永远不会迭代超过现在应该删除的过期条目。

不需要程序定期唤醒的设计通常比要求定期唤醒的设计更可取,尤其是在笔记本电脑和手机等功率受限的平台上。 如果您的程序一直要求每隔一段时间被唤醒,CPU 就无法有效地休眠:)

最新更新