#!/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)
|