1/03/2014

勇者大人的記憶體池(memory pool)使用方法簡介

我認為精通活用資料結構是非常重要的內功!(我也還在學...)

我設計的記憶體池管理類別,就是善用資料結構的產物。(哈~自賣自誇)

 任何種類的記憶體池管理類別都是為了解決某一種需求而設計出來的。
當然,我這東西仍在持續改進中...

我只是單純的分享一下我的設計方式,網友若有更好的設計方式,
也麻煩分享給我喔!


有網友就問我該如何使用這些記憶體池管理類別呢!?
那我就介紹一下一些我常用的記憶體池管理類別。

記憶體池管理類別  使用範例如下:
void Example()
{
    // AV_KernelMemoryPool 記憶體池管例類別用法簡介
 
    DWORD bytes_1MB = (1024 * 1024);
    DWORD memory_pool_bytes_max = bytes_1MB * 100;
    // 先配置100MB的總記憶體池
    AV_KernelMemoryPool::sMemoryPoolCreate( memory_pool_bytes_max );
 
    //#####################################################
    // AV_MemoryPool2HeadList 雙向鍊結串列用法簡介
 
    struct PlayerInfo // 玩家資訊結構- 假設有此種結構
    {
        DWORD Number;
        char  Name[ 16 ];
        DWORD Attack;
        DWORD Defend;
    };
   
    // 此PlayerTable表格實際上會是定義在例如GameManager這種主類別之中,
    // 現在這只是範例,所以就寫在這裡.
    AV_MemoryPool2HeadList< PlayerInfo > PlayerTable; // 玩家資訊串列表格
 
    // 初始化表格
    PlayerTable.Create( 100 ); // 從總記憶體池配置出100個PlayerInfo的空間
   
    // 如果當有需要使用新PlayerInfo節點時...
    // 例如: Client端要登入Server端時, Server端就需要配置該玩家資訊的節點。
    // 下面是取得一個新的PlayerInfo節點來用。
    PlayerInfo *new_player_node = PlayerTable.GetNode();

    // 將節點串連起來(也可以不用,看當時的需求)

    // 串起來的目的是...如果有尋訪所有使用節點的需求時, 則就會用到.
    PlayerTable.Link( new_player_node );
 
    // 當不需要用時...
    // 先解開連結
    PlayerTable.Unlink( new_player_node );
    // 再將此節點清除後釋放
    // ps: 這不是真正的記憶體釋放,而是將此節點先保留起來,
    // 等到下次要GetNode()時,則會優先分配出去使用.
    PlayerTable.ReleaseNode( new_player_node );
 
    //------------------

    // 還有一種AV_ MemoryPool2HeadArrayList用法,
    // 目的是...不需要向總記憶體池來索取記憶體,

    // 而是用自身的NodeBuffer記憶體來當作串列用的記憶體.
    // 我想...這是個很有趣的觀念和用法...
    // 舉例: 假設有這樣的SceneInfo結構...
    struct SceneInfo
    {
    // 設定NPCTable有100個PlayerInfo空間,但目前還未分配到記憶體.
        AV_MemoryPool2HeadArrayList< PlayerInfo, 100 > NPCTable;
    // 設定EnemyTable有200個PlayerInfo空間,但目前還未分配到記憶體.
        AV_MemoryPool2HeadArrayList< PlayerInfo, 200 > EnemyTable;
    };
    AV_MemoryPool2HeadList< SceneInfo > SceneTable;
    // 這個5是隨便打的.
    SceneTable.Create( 5 );
    SceneInfo *scene = SceneTable.GetNode(); // 取得新節點
    // 重點在這...    // Create內部會將自身的NodeBuffer和自身的    // pBufferList連接起來, 達到記憶體配置之自給自足喔!
    scene->NPCTable.Create();      // 分配記憶體,也就是做記憶體連結初始化
    scene->EnemyTable.Create(); // 分配記憶體,也就是做記憶體連結初始化
 
    // 之後呢...萬一有需要將scene資訊清除時,
    // 可以直接這樣清除...
    ZeroMemory( scene, sizeof( SceneInfo ) );
    // 或是這樣清除並釋放...
    SceneTable.ReleaseNode( scene );
    // 而不需要再顧慮NPCTable和EnemyTable的記憶體配置釋放問題.
    // 這種設計方式能達到記憶體不用再歸還作業系統.     //#####################################################
    // AV_SimpleMemoryPool 小型記憶體池用法簡介
 
    DWORD simple_memory_pool_bytes_max = bytes_1MB * 10;
    AV_SimpleMemoryPool  SimpleMemoryPool;
    // 這小型記憶體池是從主記憶體池分配出來的.
    SimpleMemoryPool.MemoryPoolCreate( simple_memory_pool_bytes_max );
 
    // 而這要如何應用呢!?
    // 就用上面的SceneInfo例子來應用.
    AV_MemoryPool2HeadList< SceneInfo > SceneTable2;
    DWORD scene_table_node_count = 5;
    DWORD scene_table_memory_bytes =
                sizeof( MPNode2< SceneInfo > ) * scene_table_node_count;

    // 重點來了, 從SimpleMemoryPool配置5個MPNode2< SceneInfo >節點的記憶體,
    // 然後傳給SceneTable2來作記憶體連結的初始化,
    // 這樣一來,SceneTable2萬一不需要再使用時,
    // 可以藉由SimpleMemoryPool來作全部的回收再利用 ,
    // 也就是同樣的記憶體,再給其他種的記憶體結構所使用,
    // 更能善加利用記憶體池喔.
    SceneTable2.CreateLink( scene_table_node_count,         (MPNode2< SceneInfo >*)SimpleMemoryPool.MemoryAllocate(
                scene_table_memory_bytes ) );

 
    //#####################################################
    // AV_SimplyMemoryPoolArray 陣列用法簡介
 
    struct RoomInfo
    {
        DWORD Number;
        char  Name[ 16 ];
    };
 
    // 其實這用法也很簡單,跟上面的都類似...
    // 我有盡量將相似的使用語法統一起來.
 
    AV_SimplyMemoryPoolArray< RoomInfo > RoomTable;
    // 配置100個RoomInfo的實體空間
    RoomTable.Create( 100 );
 
    RoomInfo *room = RoomTable.Get( 0 );
    room->Number = 100;
 
    // 下面這是指標陣列
    AV_MemoryPoolPointerArray< RoomInfo > RoomPointerTable;
    // 配置100個RoomInfo的指標陣列空間
    RoomPointerTable.Create( 100 );
    RoomPointerTable.Add( room );

    //#####################################################

    //其他種的記憶體池管理類別也都是大同小異的用法
    //看倌們就自行發揮嚕!
    //其實,最好是自己動手寫一組這種管理類別,
    //這樣自己才能夠真正瞭解到關鍵點, 也才能自己解決各種需求喔!

    //#####################################################

 
    // 當程式要結束時,就這樣將記憶體池釋放給作業系統吧.
    AV_KernelMemoryPool::sMemoryPoolRelease();
}



謝謝指教!

沒有留言: