網游服務器的邏輯一般來說比較復雜,而且在很多情況下還使用了多線程,因此使用基於引用計數的智能指針能很大程度的減少內存泄漏和對象失效等問提.
但是基於引用計數的指針在很多情況下也會產生另一種情況的泄漏,例如:網游中有一個代表角色的類型character,角色的對象在網游中可以說是最常見的對象
之一了,與幾乎所有的游戲邏輯都有關系,因此,一個角色對象可能會存在於很多容器,或被其它的對象所持有,為了保證角色對象的有效性,常常會使用基
於引用計數的智能指針來存放角色對象。問題由此產生了,例如當一個角色離開地圖,我們就需要把所有指向這個角色的智能指針清0,稍有不甚,漏掉一個都
會導致資源無法釋放,而且要找到具體還被哪些地方持有是相當麻煩的事情.
在我們項目中,處理這種情況的做法是,不使用智能指針來存放對象,而是采用了另外一種叫做ident的對象.對象被創建之后,只由一個容器持有其原始對象,
其它的所有外部引用持有的都是那個由原始對象產生的ident對像.當邏輯需要使用原始對象時,通過ident轉換成原始對象,如果原始對象依舊有效則返回原始
對象,否則,返回NULL。首先,原始對象是基於引用計數的對象,其中有一個64位的identity成員,其高32位是一個全局計數器的值,低32位是一個時間戳.對象被創建的時候,
identity被正確的初始化,被銷毀時將identity置0,這樣,兩個對象identity擁有相同值的概率是相當低的.
然后看下ident對象,只有一備份的identity和一個指向原始對象的指針,通過make_ident函數,可以通過一個原始對象的指針產生一個ident對象.然后,
可以通過cast_2_refbase將一個ident對象轉換回原始指針,如果轉換成功,原始對象的引用加1,防止對象正在使用的時候被其它線程釋放掉.只要在
使用完畢后調用ref_decrease清理即可.
refbase.h
#ifndef _REFBASE_H #define _REFBASE_H #include <stdint.h> #include <stdlib.h> #include <time.h> #include <sys/time.h> #include "atomic.h" extern atomic_32_t global_counter; struct refbase { atomic_32_t refcount; atomic_64_t identity; atomic_32_t flag; void (*destroyer)(void*); }; void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount); static inline atomic_32_t ref_increase(struct refbase *r) { return ATOMIC_INCREASE(&r->refcount); } static inline atomic_32_t ref_decrease(struct refbase *r) { atomic_32_t count; if((count = ATOMIC_DECREASE(&r->refcount)) == 0){ r->identity = 0; _FENCE; int32_t c = 0; for(;;){ if(COMPARE_AND_SWAP(&r->flag,0,1)) break; if(c < 4000){ ++c; __asm__("pause"); }else{ struct timespec ts = { 0, 500000 }; nanosleep(&ts, NULL); } } r->destroyer(r); } return count; } typedef struct ident { uint64_t identity; struct refbase *ptr; }ident; static inline ident make_ident(struct refbase *ptr) { ident _ident = {ptr->identity,ptr}; return _ident; } static inline ident make_empty_ident() { ident _ident = {0,NULL}; return _ident; } static inline struct refbase *cast_2_refbase(ident _ident) { while(_ident.identity == _ident.ptr->identity) { if(COMPARE_AND_SWAP(&_ident.ptr->flag,0,1)) { struct refbase *ptr = NULL; if(_ident.identity == _ident.ptr->identity && ref_increase(_ident.ptr) > 0) ptr = _ident.ptr; _FENCE; _ident.ptr->flag = 0; return ptr; } } return NULL; } static inline int32_t is_vaild_ident(ident _ident) { if(!_ident.ptr || !_ident.identity) return 0; return 1; } #define TO_IDENT(OTHER_IDENT) (*(ident*)&OTHER_IDENT) #endif
refbase.c
#include "refbase.h" #include "SysTime.h" atomic_32_t global_counter = 0; void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount) { r->destroyer = destroyer; r->identity = ATOMIC_INCREASE(&global_counter); r->identity <<= 32; r->identity += GetSystemMs(); r->refcount = initcount; }
大致處理邏輯如下:
ident _ident; struct ref_base *atker = cast_2_refbase(_ident); if(atker) { //對象依然有效,執行某些邏輯 ...... ref_decrease(&atker);//不再使用了,減少計數 } else { //原對象已經失效 }