c++;过期的映射项线程与事件循环 我正在寻找一个关于C++设计问题的建议。关于这个问题的一些背景

c++;过期的映射项线程与事件循环 我正在寻找一个关于C++设计问题的建议。关于这个问题的一些背景,c++,multithreading,runnable,event-loop,C++,Multithreading,Runnable,Event Loop,我有如下所示的可运行类: class Runnable { public: Runnable(); virtual ~Runnable(); void Stop(); void Start(); Runnable(Runnable const&) = delete; Runnable& operator =(Runnable const&) = delete; protected: virtual void Run(

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

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类的第三个类。这个类封装了std::无序映射

模板

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;
};
classmymap:public ExpirationMap
{
公众:
void DoExpire(uint8_t)覆盖;
void Init(uint8_t);
无效添加(常量键类型、常量值类型和);
ValueType Get(常量键类型);
bool存在(常量键类型);
ValueType移除(const KeyType);
无效清除();
...
私人:
std::无序地图mMap;
std::共享锁;
};
MyMap::Init启动ExpirationMap::Init,它生成一个以MyMap::DoExpire作为线程函数的线程。DoExpire基本上是一个永无止境的while循环。线程的基本工作是扫描MyMap的元素并删除过期的条目。映射的每个元素(值)都有一个过期时间,用于检查元素是否是过期的候选元素。所有这些都得到了实施,并且运行良好

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

我认为您可以做得比这两种方法都好——您可以这样做,这样您就不需要定期做任何事情,因此您也不需要事件循环或更新线程

由于地图中的每个条目都有一个与之相关联的过期时间,因此您只需围绕地图对象构建一个API层,假装过期对象已不存在,例如(伪代码):

bool ExpirationMap::Exists(const-KeyType&key)const
{
如果(mMap.has_key(key)==false)返回false;
return(mMap[key].mExpirationTime
这足以获得您想要的行为;唯一剩下的问题(这可能是一个实际问题,也可能不是,取决于您的用例)是,随着时间的推移,映射可能会变得很大,充满了无用的旧/过期条目。这可以通过多种方式处理(包括忽略问题,如果内存使用不是问题,或者仅在查找条目并发现其已过期时删除条目),但接近最佳的处理方式是保留第二个内部数据结构(例如,一个std::priority_队列,它保存按过期时间排序的条目);然后,无论何时调用任何方法,您都可以执行以下操作:

while(mEntriesByExpirationTime.size() > 0)
{
    const ByTimeEntry & firstEntry = mEntriesByExpirationTime.begin();
    if (firstEntry.mExpirationTime < now)
    {
        mMap.erase(firstEntry.mKey);
        mEntriesByExpirationTime.pop();
    }
    else break;
}
while(mEntriesByExpirationTime.size()>0)
{
const ByTimeEntry&firstEntry=mEntriesByExpirationTime.begin();
if(firstEntry.mExpirationTime<现在)
{
mMap.erase(firstEntry.mKey);
mEntriesByExpirationTime.pop();
}
否则就断了;
}
…由于此优先级_队列中的条目是按过期时间顺序保存的,因此此调用尽可能便宜,因为它不会迭代超过应该立即删除的过期条目


一般来说,不需要程序定期唤醒的设计比需要唤醒的设计更可取,尤其是在笔记本电脑和手机等电源受限的平台上。如果你的程序一直要求经常唤醒,CPU就无法有效睡眠:)

Jeremy,谢谢你的建议。我已经听从了你的第一个建议,因为exp.thread可以落后。不幸的是,内存是一个问题。参赛作品的寿命很短(几秒或几分钟),参赛作品的数量很快就会变得很大。因此有必要进行簿记。关于拥有一个单独的PQ,这是否会在插入(在映射和PQ中插入)期间以及可能在查找期间增加惩罚,即需要扫描PQ并删除条目。因为这将在服务器上运行,所以我并不特别关心节能问题;我怀疑拥有一个单独的线程(因此必须对每个操作执行适当的锁定)会比不需要单独线程的解决方案增加更多的开销。
 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();
 }
while(mEntriesByExpirationTime.size() > 0)
{
    const ByTimeEntry & firstEntry = mEntriesByExpirationTime.begin();
    if (firstEntry.mExpirationTime < now)
    {
        mMap.erase(firstEntry.mKey);
        mEntriesByExpirationTime.pop();
    }
    else break;
}