2017-04-06 18 views
0

我找上我有一個C++設計問題一些建議。C++到期地圖項線程VS事件循環

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類的ExpirationMap:在這個問題上的一些背景...

我已經運行的類如波紋管

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中::初始化序幕ExpirationMap ::初始化它滋生了與MyMap中一個線程:: DoExpire作爲線程函數。 MyMap :: DoExpire基本上是一個永不停止的while循環。線程的基本工作是掃描MyMap的元素並刪除過期的條目。地圖的每個元素(值)都有一個過期時間,用於檢查元素是否是過期的候選人。所有這些都已實施並運行良好。

對不起,長的介紹,但現在到了真正的問題。 現在,我有一個情況我有港口此代碼的事件循環的平臺。由於事件循環系統支持帶回調的定時器,因此我可以將DoExpire函數作爲定時器函數的回調。不過,我想看看是否有這樣的代碼工作在一個更好的方式來重構代碼,即都基於線程的平臺(我現在)和事件循環,同時儘量減少重複爲主。創建MyMap時,我希望能夠說:創建一個使用基於線程的過期或基於定時器+回調的過期的映射。任何建議或建議,不勝感激。謝謝。

回答

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條目的過期時間順序舉行,這一呼籲是便宜的,因爲它可以,因爲它會永遠不要迭代現在應該刪除的過期條目。

不需要你的程序定期喚醒一個設計一般都在一個沒有,特別是在如筆記本電腦和手機功率受限的平臺最好。如果你的程序一直要求每隔一段時間都要喚醒,CPU無法高效地睡眠:)

+0

傑里米,謝謝你的建議。自從exp以來,我已經遵循你的第一個建議。線程可以落後。不幸的是,記憶是一個問題。參賽作品是短暫的(幾秒或者幾分鐘),並且參賽作品可以非常快地變得相當大。因此需要做記錄。 關於有一個單獨的PQ,不會在插入過程中插入(插入映射和PQ)並且也可能在查找期間添加罰分,即需要掃描PQ並刪除條目。由於這將在服務器上運行,所以我並不特別關心節能。 –

+0

任何解決方案都會增加開銷;我懷疑擁有一個單獨的線程(因此必須對每個操作進行適當的鎖定)會比不需要單獨線程的解決方案增加更多開銷。 –