c ++ Expiring map entries线程vs event-loop

时间:2017-04-06 01:08:05

标签: c++ multithreading runnable event-loop

我正在寻找有关我正在使用的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时,我希望能够说:创建一个使用基于线程的到期或基于计时器+回调的到期的地图。非常感谢任何建议或意见。感谢。

1 个答案:

答案 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无法有效地睡眠:)