#!/usr/bin/python # -*- coding: GBK -*- #------------------------------------------------------------------------------- # ##@package ObjPool # # @todo:¶ÔÏ󳨹ÜÀí # @author hxp # @date 2025-08-06 # @version 1.0 # # ÏêϸÃèÊö: ¶ÔÏ󳨹ÜÀí£¬½¨Òé¶ÔƵ·±´´½¨µÄÀàʹÓöÔÏ󳨣¬Èç·â°üÏà¹Ø¡¢Õ½¶·Ïà¹Ø # #------------------------------------------------------------------------------- #"""Version = 2025-08-06 18:30""" #------------------------------------------------------------------------------- import GameWorld import PyGameData import collections import types #import sys #import ctypes class RecursiveObjectPoolManager(object): def __init__(self): self._pools = {} # ´æ´¢¸÷ÀàµÄ¶ÔÏó³Ø {class: pool} # ʹÓöÔÏóID×÷Ϊ¼üµÄÓ³É䣺{id(obj): obj_class} self._obj_id_to_pool = {} self._releasing = set() # ÕýÔÚÊͷŵĶÔÏóID¼¯ºÏ def create_pool(self, obj_class, max_size=10): """Ϊָ¶¨Àà´´½¨¶ÔÏó³Ø :param obj_class: Òª¹ÜÀíµÄ¶ÔÏóÀà :param max_size: ¶ÔÏó³Ø×î´óÈÝÁ¿ """ if obj_class in self._pools: GameWorld.Log("Pool for %s already exists" % obj_class.__name__) return GameWorld.Log("create_pool %s, max_size=%s" % ( obj_class.__name__, max_size)) self._pools[obj_class] = { 'max_size': max_size, 'free_list': [], # ¿ÕÏжÔÏóÁбí 'active_list': [], # ʹÓÃÖжÔÏóÁбí 'created_count': 0 # ÒÑ´´½¨¶ÔÏó¼ÆÊý } return self def acquire(self, obj_class, *args, **kwargs): """»ñÈ¡¶ÔÏ󲢼ǼÆäËùÊô³Ø""" if obj_class not in self._pools: # Èç¹û³Ø²»´æÔÚ£¬×Ô¶¯´´½¨´óСΪ0µÄÎÞÏÞÖÆ³Ø self.create_pool(obj_class, 0) pool = self._pools[obj_class] if pool['free_list']: # ¸´ÓÿÕÏжÔÏó obj = pool['free_list'].pop() # ÖØÐ³õʼ»¯¶ÔÏó obj.__init__(*args, **kwargs) else: # ´´½¨Ð¶ÔÏó if pool['max_size'] <= 0 or pool['created_count'] < pool['max_size']: obj = obj_class(*args, **kwargs) pool['created_count'] += 1 # ¼Ç¼¶ÔÏ󵽳صÄÓ³Éä obj_id = id(obj) self._obj_id_to_pool[obj_id] = obj_class else: GameWorld.ErrLog("%s pool exhausted" % obj_class.__name__) return None # ·µ»ØNone±íʾ»ñȡʧ°Ü pool['active_list'].append(obj) return obj def release(self, obj): """ÊͷŶÔÏ󲢵ݹéÊÍ·ÅÆäǶÌ×¶ÔÏ󳨶ÔÏó""" obj_id = id(obj) # ¼ì²éÊÇ·ñÕýÔڵݹéÊÍ·ÅÖÐ if obj_id in self._releasing: return # Ö»´¦Àí¶ÔÏ󳨹ÜÀíµÄ¶ÔÏó if obj_id not in self._obj_id_to_pool: return # ±ê¼ÇΪÕýÔÚÊÍ·Å self._releasing.add(obj_id) visited = set([obj_id]) # ³õʼ»¯ÒÑ·ÃÎʼ¯ºÏ try: # 1. µÝ¹éÊÍ·ÅǶÌ×¶ÔÏ󳨶ÔÏó self._recursive_release(obj, visited) # 2. Êͷŵ±Ç°¶ÔÏó self._return_to_pool(obj) finally: # ÒÆ³ýÊͷűê¼Ç self._releasing.discard(obj_id) def _recursive_release(self, obj, visited): """µÝ¹éÊÍ·ÅǶÌ×¶ÔÏ󳨶ÔÏó£¨Éî¶ÈÓÅÏÈ£©""" # »ñÈ¡¶ÔÏóµÄËùÓÐÒýÓ㨲»°üº¬»ù´¡ÀàÐÍ£© for ref in self._get_references(obj): try: ref_id = id(ref) except TypeError: # Èç¹û¶ÔÏ󲻿ɹþÏ££¬Ìø¹ý continue if ref_id in visited: continue visited.add(ref_id) # Ö»´¦Àí¶ÔÏ󳨹ÜÀíµÄ¶ÔÏó if ref_id in self._obj_id_to_pool: # ÏȵݹéÊÍ·ÅǶÌ×¶ÔÏó self._recursive_release(ref, visited) # ÔÙÊͷŵ±Ç°ÒýÓöÔÏó self._return_to_pool(ref) else: # ¶ÔÓڷǶÔÏ󳨹ÜÀíµÄ¶ÔÏ󣬼ÌÐøµÝ¹é²éÕÒÆäÖеĶÔÏ󳨶ÔÏó self._recursive_release(ref, visited) def _get_references(self, obj): """°²È«»ñÈ¡¶ÔÏóµÄËùÓÐÖ±½ÓÒýÓã¬Ö§³Ö¸´ÔÓÊý¾Ý½á¹¹""" refs = [] obj_type = type(obj) # ´¦Àí×Ô¶¨Òå¶ÔÏóµÄÊôÐÔ try: # Ê×Ïȳ¢ÊÔ__dict__ÊôÐÔ if hasattr(obj, '__dict__'): for attr in dir(obj): if attr.startswith('__'): continue try: attr_value = getattr(obj, attr) except Exception: continue # ¹ýÂË»ù´¡ÀàÐÍ if not self._is_basic_type(attr_value): refs.append(attr_value) except Exception: pass # ´¦Àí³£¼ûÈÝÆ÷ÀàÐÍ if obj_type in (list, tuple, set, collections.deque): for item in obj: if not self._is_basic_type(item): refs.append(item) elif obj_type is dict: for key, value in obj.iteritems(): if not self._is_basic_type(key): refs.append(key) if not self._is_basic_type(value): refs.append(value) # ´¦ÀíÌØÊâÊôÐÔ£¨Èç__slots__¶¨ÒåµÄ¶ÔÏó£© try: if hasattr(obj, '__slots__'): for slot_name in obj.__slots__: if hasattr(obj, slot_name): slot_value = getattr(obj, slot_name) if not self._is_basic_type(slot_value): refs.append(slot_value) except Exception: pass return refs def _is_basic_type(self, value): """ÅжÏÖµÊÇ·ñΪ»ù±¾ÀàÐÍ£¬²»ÐèÒªµÝ¹é´¦Àí""" if value is None: return True if isinstance(value, (int, long, float, bool, str, unicode)): return True if isinstance(value, (types.FunctionType, types.MethodType, types.ModuleType)): return True if isinstance(value, (types.BuiltinFunctionType, type)): return True return False def _return_to_pool(self, obj): """½«¶ÔÏó¹é»¹µ½ÆäËùÊô³Ø""" obj_id = id(obj) # »ñÈ¡¶ÔÏóËùÊôÀà if obj_id not in self._obj_id_to_pool: return # Èç¹û²»ÔÚ³ØÖУ¬ºöÂÔ obj_class = self._obj_id_to_pool[obj_id] if obj_class not in self._pools: return # Èç¹û³Ø²»´æÔÚ£¬ºöÂÔ pool = self._pools[obj_class] # È·±£¶ÔÏóÔÚ»îÔ¾ÁбíÖÐ if obj in pool['active_list']: # ´Ó»îÔ¾ÁбíÒÆ³ý try: pool['active_list'].remove(obj) except ValueError: # Èç¹û¶ÔÏó²»ÔÚÁбíÖУ¬ºöÂÔ return # Ö´ÐжÔÏóÇåÀí·½·¨ #if hasattr(obj, 'cleanup'): # try: # obj.cleanup() # except Exception: # pass # Ìí¼Óµ½¿ÕÏÐÁбí pool['free_list'].append(obj) else: # Èç¹ûÔÚ¿ÕÏÐÁбíÖУ¬Ìø¹ý pass def destroy_pool(self, obj_class): """Ïú»ÙÖ¸¶¨ÀàµÄ¶ÔÏó³Ø""" if obj_class not in self._pools: return pool = self._pools[obj_class] # ÇåÀíËùÓжÔÏó for obj in list(pool['free_list'] + pool['active_list']): # ÇåÀí¶ÔÏóÊôÐÔ #if hasattr(obj, 'cleanup'): # try: # obj.cleanup() # except Exception: # pass # ´ÓÓ³ÉäÖÐÒÆ³ý obj_id = id(obj) if obj_id in self._obj_id_to_pool: del self._obj_id_to_pool[obj_id] del self._pools[obj_class] def pool_status(self): """Êä³öËùÓжÔÏ󳨵Ä״̬ÐÅÏ¢""" statusList = [] max_name_len = 0 max_created_len = 0 max_free_len = 0 max_active_len = 0 # ÊÕ¼¯×´Ì¬²¢¼ÆËã×î´ó³¤¶È for obj_class, pool in self._pools.items(): # »ñÈ¡ÀàÃû class_name = getattr(obj_class, '__name__', str(obj_class)) created = pool['created_count'] free = len(pool['free_list']) active = len(pool['active_list']) statusList.append({ 'obj_class': class_name, 'created': created, 'free': free, 'active': active }) # ¼ÆËã×î´ó³¤¶È max_name_len = max(max_name_len, len(class_name)) max_created_len = max(max_created_len, len(str(created))) max_free_len = max(max_free_len, len(str(free))) max_active_len = max(max_active_len, len(str(active))) # Ìí¼Ó±íÍ·³¤¶È max_name_len = max(max_name_len, 8) # "¶ÔÏó³ØÃû³Æ"µÄ³¤¶È max_created_len = max(max_created_len, 4) # "×ÜÊý"µÄ³¤¶È max_free_len = max(max_free_len, 4) # "¿ÕÏÐ"µÄ³¤¶È max_active_len = max(max_active_len, 4) # "»îÔ¾"µÄ³¤¶È # °´»îÔ¾¶ÔÏóÊýÁ¿ÅÅÐò statusList.sort(key=lambda s: (s['created'], s['active']), reverse=True) # ´´½¨¸ñʽ×Ö·û´® header_format = "| {:<{name_width}} | {:>{created_width}} | {:>{free_width}} | {:>{active_width}} |" row_format = "| {:<{name_width}} | {:>{created_width}} | {:>{free_width}} | {:>{active_width}} |" # Êä³ö±íÍ· GameWorld.Log("----- ¶ÔÏó³Ø×´Ì¬ -----") GameWorld.Log(header_format.format( "¶ÔÏó³ØÃû³Æ", "×ÜÊý", "¿ÕÏÐ", "»îÔ¾", name_width=max_name_len, created_width=max_created_len, free_width=max_free_len, active_width=max_active_len )) # Êä³ö·Ö¸ôÏß separator = "+-{}-+-{}-+-{}-+-{}-+".format( "-" * max_name_len, "-" * max_created_len, "-" * max_free_len, "-" * max_active_len ) GameWorld.Log(separator) # Êä³öÊý¾ÝÐÐ for s in statusList: GameWorld.Log(row_format.format( s['obj_class'], s['created'], s['free'], s['active'], name_width=max_name_len, created_width=max_created_len, free_width=max_free_len, active_width=max_active_len )) return def GetPoolMgr(): """»ñȡָ¶¨Àà´´½¨¶ÔÏó³Ø""" poolMgr = PyGameData.g_objPoolMgr if not poolMgr: poolMgr = RecursiveObjectPoolManager() PyGameData.g_objPoolMgr = poolMgr return poolMgr def OnMinute(): """ÿ·ÖÖÓÖ´ÐУ¬Êä³ö¶ÔÏó³Ø×´Ì¬""" GetPoolMgr().pool_status() return ## ʹÓÃʾÀý #if __name__ == "__main__": # # # ¶¨ÒåÒ»¸öǶÌ×µÄStructureÀà # class SubStructure(ctypes.Structure): # _fields_ = [("z", ctypes.c_int)] # # def __init__(self, name): # self.name = name # print "³õʼ»¯SubStructure: name=%s" % (self.name) # return # # def cleanup(self): # """ÇåÀí·½·¨""" # print "ÇåÀíSubStructure: name=%s" % (self.name) # self.name = None # # # ¶¨ÒåÒ»¸ö¼Ì³Ð×ÔStructureµÄÀà # class MyStructure(ctypes.Structure): # _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)] # # def __init__(self, name): # self.name = name # # ǶÌ×µÄStructure¶ÔÏó # self.sub = GetPoolMgr().acquire(SubStructure, "%s_sub" % name) # print "³õʼ»¯MyStructure: name=%s" % (self.name) # return # # def cleanup(self): # """ÇåÀí·½·¨""" # print "ÇåÀíMyStructure: name=%s" % (self.name) # self.name = None # # ²»ÐèÒªÊÖ¶¯ÊÍ·Åsub£¬µÝ¹éÊͷŻᴦÀí # self.sub = None # # class ClassE(): # # def __init__(self, name): # self.name = name # print "³õʼ»¯¶ÔÏó ClassE: name=%s" % (self.name) # self.ed1 = GetPoolMgr().acquire(ClassD, "%s_ed_1" % name) # return # # class ClassD(): # # def __init__(self, name): # self.name = name # print "³õʼ»¯¶ÔÏó ClassD: name=%s" % (self.name) # return # # def cleanup(self): # """ÇåÀí·½·¨""" # print "ÇåÀíClassD: name=%s" % (self.name) # self.name = None # # class ClassC(): # # def __init__(self, name, bb): # self.name = name # print "³õʼ»¯¶ÔÏó ClassC: name=%s" % (self.name) # self.bb = bb # self.cd1 = GetPoolMgr().acquire(ClassD, "%s_cd_1" % name) # self.cd2 = GetPoolMgr().acquire(ClassD, "%s_cd_2" % name) # return # # class ClassB(): # # def __init__(self, name): # self.name = name # print "³õʼ»¯¶ÔÏó ClassB: name=%s" % (self.name) # bc1 = GetPoolMgr().acquire(ClassC, "%s_c_1" % name, self) # bc2 = GetPoolMgr().acquire(ClassC, "%s_c_2" % name, self) # self.cList = [bc1, bc2] # self.dDict = {1:GetPoolMgr().acquire(ClassD, "%s_d_1" % name), GetPoolMgr().acquire(ClassD, "%s_d_2" % name):2, "bc1":bc1, "bc2":bc2} # return # # def do(self, doStr): # print "ClassB.do name=%s, %s" % (self.name, doStr) # return # # class ClassA(): # # def __init__(self, objID, name): # self.objID = objID # self.name = name # self.b = GetPoolMgr().acquire(ClassB, "%s_b" % name) # self.e = ClassE("%s_e" % name) # self.struct = GetPoolMgr().acquire(MyStructure, "%s_struct" % name) # Structure¶ÔÏó # self.dDict = {1:GetPoolMgr().acquire(ClassD, "%s_d_1" % name), GetPoolMgr().acquire(ClassD, "%s_d_2" % name):2} # print "³õʼ»¯¶ÔÏó ClassA: objID=%s,name=%s" % (self.objID, self.name) # # def do(self, doStr): # print "ClassA.do objID=%s,name=%s, %s" % (self.objID, self.name, doStr) # return # # def cleanup(self): # """ÇåÀí·½·¨""" # print "ÇåÀíClassA: objID=%s,name=%s" % (self.objID, self.name) # self.b = None # self.e = None # self.struct = None # self.dDict = None # self.name = None # self.objID = None # # # # ´´½¨¶ÔÏ󳨹ÜÀíÆ÷ # poolMgr = GetPoolMgr() # # # ΪËùÓÐÀà´´½¨¶ÔÏ󳨣¬max_size=0 ±íʾÎÞÏÞÖÆ # poolMgr.create_pool(ClassA, max_size=0) # poolMgr.create_pool(ClassB, max_size=0) # poolMgr.create_pool(ClassC, max_size=0) # poolMgr.create_pool(ClassD, max_size=0) # poolMgr.create_pool(MyStructure, max_size=0) # poolMgr.create_pool(SubStructure, max_size=0) # ǶÌ×µÄStructureÀà # # # ´´½¨²âÊÔ¶ÔÏó # a1 = poolMgr.acquire(ClassA, 1000, "hanmeimei") # if a1 is None: # GameWorld.ErrLog("Failed to acquire ClassA instance for hanmeimei") # else: # a1.do("hello 1ilei") # # a2 = poolMgr.acquire(ClassA, 1001, "lilei") # if a2 is None: # GameWorld.ErrLog("Failed to acquire ClassA instance for lilei") # else: # a2.do("hello hanmeimei") # # # Êä³ö״̬ # print("\nÊÍ·Åǰ״̬") # poolMgr.pool_status() # # # ÊͷŶÔÏó # if a1: # poolMgr.release(a1) # if a2: # poolMgr.release(a2) # # # Êä³ö״̬ # print("\nÊͷźó״̬") # poolMgr.pool_status() # # # ÔٴλñȡӦ¸´ÓöÔÏó # a3 = poolMgr.acquire(ClassA, 1002, "lihua") # if a3 is None: # GameWorld.ErrLog("Failed to acquire ClassA instance for lihua") # else: # a3.do("fuck you") # # # Êä³ö״̬ # print("\nÔٴλñÈ¡ºó״̬") # poolMgr.pool_status() # # # ÇåÀí # if a3: # poolMgr.release(a3) # poolMgr.destroy_pool(ClassA) # poolMgr.destroy_pool(ClassB) # poolMgr.destroy_pool(ClassC) # poolMgr.destroy_pool(ClassD) # poolMgr.destroy_pool(MyStructure) # poolMgr.destroy_pool(SubStructure)