[C++] 一個能夠定時自毀的類的實現

試想一下, 有沒有這種需求:

對於每一個新的對象, 我們希望它能夠在一定時間後自動銷燬, 前提是我們沒有在這段時間內給它發出重置信號.

這種需求其實是有的, 比如在電影裏, 主角知道了一個反派不希望被揭露的祕密, 同時需要保住自己的性命, 那麼就可以構造這樣一個對象, 如果24小時內主角不給這個對象發送重置的信號, 它就會將這個祕密公之於衆. 再比如, 在網絡應用場景裏, 我們希望每一個客戶端能夠定時給我們發送心跳包, 如果長時間不發送的話, 我們就剔除這個客戶.

在之前的文章裏, 我嘗試使用了WIN32的Timer, 但是發現這種做法非常繁瑣且容易出錯, 你需要給每個對象綁定一個Timer, 同時需要在Timer到期時處理對象, 並且重置Timer的API和設置Timer的API是同一個, 稍有不慎就會搞砸.

現在, 我想出了一種相對簡單的實現方式, 雖然精度不是非常理想, 但對於一般應用而言, 足矣.


 

我們構造一個類, 它有一些私有的數據, 這些可以自定義, 但有一些API是必須的:

class Client
{
private:
    // ...Data or something
    int32_t m_life;
    int32_t m_max_life;
    DWORD delete_thread_id;
    HANDLE count_thread_handle;
public:                            
    Client(int32_t, DWORD);
    void reset(void);
    static WIN32API DWORD countDownEntry(void *);
    DWORD countDown(void);
    // ...De-cons...
}

 

1. 構造函數:

Client:Client(int32_t life, DWORD thread_id)
{
    m_max_life = m_life = life;
    delete_thread_id = thread_id;
    count_thread_handle = CreateThread(..., ..., Client::countDownEntry, this);
}

第二個參數是用來銷燬對象的線程ID, 這樣設計是考慮到對象有可能保存在一個堆, 如果我們簡單地調用析構函數, 那麼對象本身所佔據的空間就無法被釋放了, 所以我們通知這麼一個線程來完成所有的析構操作.

注意到我們使用的是countDownEntry()而不是countDown(), 因爲CreateThread不接受一個非靜態的成員函數作爲函數入口(無法確認地址).

2. reset()方法, 這方法需要先掛起倒計時的線程, 主要是防止同時訪問同一個內存的情況出現:

void Client::reset(void)
{
SuspendThread(count_thread_handle); m_life
= m_max_life;
ResumeThread(count_thread_handle); }

3. countDownEntry()方法爲何是static的? 很簡單, 我們需要在構造函數裏使用它來初始化倒計時線程, 而它的實現非常簡單, 我們在構造函數裏把this指針傳遞給這個靜態方法, 並在靜態方法裏重新獲取這個this代表的對象, 調用這個對象的倒計時函數即可:

static WIN32API DWORD Client::countDownEntry(void *pM)
{
    Client *c = (Client *) pM;
    return c->countDown();
}

4. 而countDown()方法更加簡單, 使用Sleep函數來計時即可, 每計一秒就將life減1:

DWORD Client::countDown()
{
    while (m_life > 0)
    {
         Sleep(1000);
         m_life--;
    }
    PostThreadMessageA(delete_thread_id);
    return 0;
}

以上就是這樣一個對象的設計思路, 原理比較簡單, 也只是寫了個大概, 同時需要windows.h的支持.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章