本文章由cartzhang编写。转载请注明出处。 所有权利保留。


文章链接:http://blog.csdn.net/cartzhang/article/details/55051570
作者:cartzhang


simple pool 博客一的地址:
http://blog.csdn.net/cartzhang/article/details/54096845

一、simpe_pool对象池的问题


在上篇对象池simple_pool中提到了它如今的问题。



一个是数据控制,也就是在单个父亲节点下,仅仅会一直增加来满足当前游戏对对象池内的对象数量的须要,没有考虑到降低。也就是说,若在A阶段,游戏场景中同一时候须要大量的某个池内O1对象,这时候就出现大量的AO1对象在内存中。可是过了A阶段,不须要A这么多O1对象时候,对象池内没有做操作和优化。



还有一个问题,就是多线程的问题。这个暂时不这里讨论。

有须要能够自己先对linkedlist加锁。

本片针对第一个问题。来做了处理。



顺便给出上篇simple_pool博客地址:

http://blog.csdn.net/cartzhang/article/details/54096845

https://github.com/cartzhang/simple_pool_bench

二、对象池内数量优化思路


本着在后台处理,尽量少影响对象池的使用的原则。决定使用在增加一个线程来实现。对须要清理的池内对象进行记录和推断。然后在主线程中进行删除(destroy)操作.

由于Unity不同意在其它自己建立的线程中调用Destroy函数,所以还是要在Update中进行处理。

这里写图片描写叙述

这个是自己画的流程图,基本就是这个样子。当然里面详细參数,能够依据自己须要调整。

三、实现代码


1.首先,建立垃圾清理标记和垃圾对象列表。

// garbage clean.
private static bool clearGarbageFlag = false;
private static List<Transform> GarbageList = new List<Transform>(100);


然后标记为真。開始清理。

这里进行了加锁,避免一边删除。一边增加,这样造成野的对象。垃圾表被清空了,对象也不在对象池内列表中。



2.主线程的清理工作

 private void Update()
        {
            CleanGarbageList();
        }

        private void CleanGarbageList()
        {
            if (clearGarbageFlag)
            {
                clearGarbageFlag = false;
                lock (GarbageList)
                {
                    Debug.Assert(GarbageList.Count > 0);
                    Debug.Log("now destroy " + GarbageList.Count + " from pool" + GarbageList[0].name);
                    for (int i = 0; i < GarbageList.Count; i++)
                    {
                        Destroy(GarbageList[i].gameObject);
                    }
                    GarbageList.Clear();
                }
            }
        }


3.优化线程启动

线程在第一次调用池的时候開始启动。

然后定时检測数量,每轮会检測checkTimesForEach个对象池内的未使用的情况。对象池个数超出的部分,等待5个中的一个多次检測没有异常后,在增加到检測中,这个主要是为了防止多个对象池内,一次性增加垃圾列表中太多对象。须要一次性删掉的太多,造成主线程卡顿的情况。

当然这也不是最理想的,为了防止卡顿。每次检測循环仅仅检測到一个满足垃圾清理条件,须要处理就会停止检測跳出循环,然后进行垃圾处理。这个也是为了轻量级的删减措施。



一旦成功设置标志。就又一次计算和检測。而且在设置后,优化线程等待1秒时间,来让主线程做工作。这个时间应该是非常充裕的。


主要代码

 private static void OptimizationPool()
        {
            // check cpu time to start
            Thread.Sleep(100);
            // 检測间隔时间20秒
            float intervalTimeTodetect = 20f;
            // after how many times counts to reset count.
            // 循环检測多少次。后记录清零。

每次仅仅处理须要处理的前5个池。 const int checkTimesForEach = 5; // 暂时池管理对象 Dictionary<int, ObjectPool> poolManagerTempDic = new Dictionary<int, ObjectPool>(); System.DateTime timeCount = System.DateTime.Now; // 间隔时间内运行一次 bool eachMinuteGetDicOnce = false; // 每一个池未使用对象超过一半的标记,记录次数 Dictionary<int, int> CurrentPoolUnuseCount = new Dictionary<int, int>(); // 检測刷新次数。也是一次计数的最大时间。 int icountLoopTime = 0; Debug.Log("Thread start"); // 休眠时间 int sleepTime = 10; while (isStartThread) { Thread.Sleep(sleepTime); if (!eachMinuteGetDicOnce) { eachMinuteGetDicOnce = true; poolManagerTempDic = poolManagerDic.ToDictionary(entry => entry.Key,entry => entry.Value); // loop check 3 time to reset. if (icountLoopTime % checkTimesForEach == 0) { CurrentPoolUnuseCount.Clear(); icountLoopTime = icountLoopTime > 10000000 ?

0 : icountLoopTime; } // mark unuse nuber for all. foreach(var element in poolManagerTempDic) { ObjectPool opool = element.Value; int unusinglinkCount = opool.UnusingLinkedList.Count; // half of all is useless and more than 10. if (unusinglinkCount * 2 > unusinglinkCount + opool.UsingLinkedList.Count && unusinglinkCount > 10) { MarkCountForUnusingLink(ref CurrentPoolUnuseCount, element.Key); // satisfy the condition,add unusing link gameobject to garbagelist. int currentMark = 0; CurrentPoolUnuseCount.TryGetValue(element.Key,out currentMark); // be marked three times, add to garbage list. if (currentMark >= 3) { AddObjectsToGarbageList(ref opool.UnusingLinkedList); // count tick to reset. CurrentPoolUnuseCount[element.Key] = 0; clearGarbageFlag = true; // each time only gathing one pool to process. break; } } } } // leave time for mainthread to delete gameobjects. if (clearGarbageFlag) { icountLoopTime = 0; intervalTimeTodetect = 20f; Thread.Sleep(1000); timeCount = System.DateTime.Now; } // interval 20 seconds to start check once; if ((System.DateTime.Now - timeCount).TotalSeconds > intervalTimeTodetect) { timeCount = System.DateTime.Now; eachMinuteGetDicOnce = false; poolManagerTempDic.Clear(); icountLoopTime++; Debug.Log("Loop count is " + icountLoopTime); } // long time nothing happen, expand the detective interval time (max <= 90s). if (icountLoopTime >= 4 ) { intervalTimeTodetect = intervalTimeTodetect * 2 >= 90f ? 90f : intervalTimeTodetect * 2; icountLoopTime = 0; Debug.Log("interval time is " + intervalTimeTodetect); } } return; } /// <summary> /// add last gameobject to garbagelist,as when unsing unusinglink is from first place to get. /// </summary> /// <param name="list"></param> private static void AddObjectsToGarbageList(ref LinkedList<Transform> list) { Debug.Assert(list.Count > 0); int FlagDestroyNumber = list.Count>>1; for (int i = 0; i < FlagDestroyNumber; i++) { GarbageList.Add(list.Last.Value); list.RemoveLast(); } }


依照眼下设置參数,開始检測的时间间隔为20秒,若20*2=40秒后,没有须要处理的垃圾,就把检測时间间隔翻倍为40秒检測一次;若在过40*4=160秒,没有触发标志。检測时间进一步延长,逐次翻倍增加。可是最大值为90秒。
也就是说。最大的检測间隔为90秒。

若中间被打断。所有归为正常20秒检測一次。


触发增加垃圾列表的条件:

设置触发标志是一个池,在3次检測中都有超过一半的对象没有被使用,而且总体未使用数量超过10个。

四、更新project分享地址:

地址:https://github.com/cartzhang/simple_pool_bench

能够下载Assets文件。然后用unity測试。

这个測试demo为 PoolTimeOptimizeOjbectsDemo.unity。

优化流程图下载地址:

https://github.com/cartzhang/simple_pool_bench/blob/master/img/pool_bench_optimize.png

若有问题,请提交问题或代码。非常感谢!

。!

五、附件

poolManager.cs 所有代码:

using UnityEngine;
using System.Collections.Generic;
using SLQJ_POOL;
using UnityEngine.Internal;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq;

/// <summary>
/// 扩展GameObject函数
/// 使用扩展函数方面代码调用,能够Unity的随意代码中,调用对象池来产生和回池对象,
/// 而且不须要调用命名空间等繁琐东西。

/// 代码样例: obj = gameObject.InstantiateFromPool(prefab); /// gameObject.DestroyToPool(poolObjs[poolObjs.Count - 1], 0.2f); /// 详细能够參考HowToUse脚本和TestEffect。

/// @cartzhang /// </summary> public static class GameObjectExten { /// <summary> /// 调用对象池,产生一对象,并带有位置和旋转等參考 /// </summary> /// <param name="gameobject"></param> /// <param name="original"></param> /// <param name="position"></param> /// <param name="rotation"></param> /// <returns></returns> public static GameObject InstantiateFromPool(this GameObject gameobject, Object original, Vector3 position, Quaternion rotation) { return PoolManager.PullObjcetFromPool(original as GameObject, position, rotation).gameObject; } /// <summary> /// 调用对象池,产生对象 /// </summary> /// <param name="gameobject"></param> /// <param name="original"></param> /// <returns></returns> public static GameObject InstantiateFromPool(this GameObject gameobject, Object original) { return PoolManager.PullObjcetFromPool(original as GameObject).gameObject; } /// <summary> /// 对象返回对象池 /// </summary> /// <param name="gameobject"></param> /// <param name="obj"></param> public static void DestroyToPool(this GameObject gameobject, Object obj) { PoolManager.PushObjectPool((obj as GameObject).transform); } /// <summary> /// 带延时的返回对象池 /// </summary> /// <param name="gameobject"></param> /// <param name="obj"></param> /// <param name="t"></param> public static void DestroyToPool(this GameObject gameobject, Object obj, [DefaultValue("0.0F")] float t) { PoolManager.PushObjectPool((obj as GameObject).transform, t); } } namespace SLQJ_POOL { public class PoolManager : MonoBehaviour { private static PoolManager instance; private static bool bStartThreadOnce = false; private static bool isStartThread = false; private static Thread tOptimizationThread; private static List<GameObject> prefabList = new List<GameObject>(); //存放预制体相应的id。ObjcetPool public static Dictionary<int, ObjectPool> poolManagerDic = new Dictionary<int, ObjectPool>(); private static Dictionary<Transform, ObjectPool> transformDic = new Dictionary<Transform, ObjectPool>(); // garbage clean. private static bool clearGarbageFlag = false; private static List<Transform> GarbageList = new List<Transform>(100); private void Update() { CleanGarbageList(); } private void CleanGarbageList() { if (clearGarbageFlag) { clearGarbageFlag = false; lock (GarbageList) { Debug.Assert(GarbageList.Count > 0); Debug.Log("now destroy " + GarbageList.Count + " from pool" + GarbageList[0].name); for (int i = 0; i < GarbageList.Count; i++) { Destroy(GarbageList[i].gameObject); } GarbageList.Clear(); } } } //初始化某个预制体相应的对象池 public static void InitPrefab(GameObject prefab, int initNum = 4) { GetObjectPool(prefab, initNum); } //外界调用的接口 public static Transform PullObjcetFromPool(GameObject prefab) { return _PullObjcetFromPool(prefab); } public static Transform PullObjcetFromPool(GameObject prefab, Vector3 pos, Quaternion quaternion) { return _PullObjcetFromPool(prefab, pos, quaternion); } private static Transform _PullObjcetFromPool(GameObject prefab) { if (prefab == null) { Debug.Log("prefab is null!"); return null; } ObjectPool objPool = GetObjectPool(prefab); StartThreadOnce(); return objPool.PullObjcetFromPool(); } private static Transform _PullObjcetFromPool(GameObject prefab, Vector3 pos, Quaternion quaternion) { if (prefab == null) { Debug.Log("prefab is null!"); return null; } ObjectPool objPool = GetObjectPool(prefab, pos, quaternion); StartThreadOnce(); return objPool.PullObjcetFromPool(pos, quaternion); } private static ObjectPool GetObjectPool(GameObject prefab, int initNum = 4) { ObjectPool objPool = null; //推断集合中是否有预制体相应的对象池 int leng = prefabList.Count; int prefabID = prefab.GetInstanceID(); for (int i = 0; i < leng; i++) { if (prefabID == prefabList[i].GetInstanceID()) { objPool = poolManagerDic[prefabID]; break; } } //没有找到对象池的话创建一个对象池 if (objPool == null) { objPool = CreatObjcetPool(prefab, initNum); } return objPool; } private static ObjectPool GetObjectPool(GameObject prefab, Vector3 pos, Quaternion qua, int initNum = 4) { ObjectPool objPool = null; int leng = prefabList.Count; int prefabID = prefab.GetInstanceID(); for (int i = 0; i < leng; i++) { if (prefabID == prefabList[i].GetInstanceID()) { objPool = poolManagerDic[prefabID]; } } if (objPool == null) { objPool = CreatObjcetPool(prefab, pos, qua, initNum); } return objPool; } private static ObjectPool CreatObjcetPool(GameObject prefab, Vector3 pos, Quaternion qua, int initNum) { prefabList.Add(prefab); GameObject go = new GameObject(); go.name = prefab.name + "Pool"; ObjectPool objPool = go.AddComponent<ObjectPool>(); objPool.InitObjectPool(prefab, pos, qua, transformDic, initNum); poolManagerDic.Add(prefab.GetInstanceID(), objPool); return objPool; } private static ObjectPool CreatObjcetPool(GameObject prefab, int initNum) { prefabList.Add(prefab); GameObject go = new GameObject(); go.name = prefab.name + "Pool"; ObjectPool objPool = go.AddComponent<ObjectPool>(); objPool.InitObjectPool(prefab, transformDic, initNum); poolManagerDic.Add(prefab.GetInstanceID(), objPool); return objPool; } public static void PushObjectPool(Transform handleTransform) { ObjectPool objPool = GetPoolByTransform(handleTransform); if (objPool) { objPool.PushObjectToPool(handleTransform); } else { GameObject.Destroy(handleTransform.gameObject); } } public static void PushObjectPool(Transform handleTransform, float delayTime) { ObjectPool objPool = GetPoolByTransform(handleTransform); if (objPool) { objPool.PushObjectToPool(handleTransform, delayTime); } else { GameObject.Destroy(handleTransform.gameObject, delayTime); } } //马上回池的接口 public static void PushObjectPool(Transform handleTransform, GameObject prefab) { ObjectPool objPool = GetObjectPool(prefab); objPool.PushObjectToPool(handleTransform.transform); } //延迟回池的接口 public static void PushObjectPool(Transform handleTransform, GameObject prefab, float delayTime) { ObjectPool objPool = GetObjectPool(prefab); objPool.PushObjectToPool(handleTransform, delayTime); } private static ObjectPool GetPoolByTransform(Transform handleTransform) { if (transformDic.ContainsKey(handleTransform)) { return transformDic[handleTransform]; } Debug.LogError(handleTransform.name + " no find it's ObjectPool"); return null; } // add code to clean pool from time to time. private static void StartThreadOnce() { // start thread to clean pool from time to time. if (!bStartThreadOnce) { bStartThreadOnce = true; ThreadPool.QueueUserWorkItem(AutoToCheckOptimization); } } private static void AutoToCheckOptimization(object obj) { Thread.Sleep(10); isStartThread = true; tOptimizationThread = new Thread(OptimizationPool); tOptimizationThread.Start(); } private static void OptimizationPool() { // check cpu time to start Thread.Sleep(100); // 检測间隔时间20秒 float intervalTimeTodetect = 20f; // after how many times counts to reset count. // 循环检測多少次,后记录清零。每次仅仅处理须要处理的前5个池。 const int checkTimesForEach = 5; // 暂时池管理对象 Dictionary<int, ObjectPool> poolManagerTempDic = new Dictionary<int, ObjectPool>(); System.DateTime timeCount = System.DateTime.Now; // 间隔时间内运行一次 bool eachMinuteGetDicOnce = false; // 每一个池未使用对象超过一半的标记,记录次数 Dictionary<int, int> CurrentPoolUnuseCount = new Dictionary<int, int>(); // 检測刷新次数。也是一次计数的最大时间。

int icountLoopTime = 0; Debug.Log("Thread start"); // 休眠时间 int sleepTime = 10; while (isStartThread) { Thread.Sleep(sleepTime); if (!eachMinuteGetDicOnce) { eachMinuteGetDicOnce = true; poolManagerTempDic = poolManagerDic.ToDictionary(entry => entry.Key,entry => entry.Value); // loop check 3 time to reset. if (icountLoopTime % checkTimesForEach == 0) { CurrentPoolUnuseCount.Clear(); icountLoopTime = icountLoopTime > 10000000 ? 0 : icountLoopTime; } // mark unuse nuber for all. foreach(var element in poolManagerTempDic) { ObjectPool opool = element.Value; int unusinglinkCount = opool.UnusingLinkedList.Count; // half of all is useless and more than 10. if (unusinglinkCount * 2 > unusinglinkCount + opool.UsingLinkedList.Count && unusinglinkCount > 10) { MarkCountForUnusingLink(ref CurrentPoolUnuseCount, element.Key); // satisfy the condition,add unusing link gameobject to garbagelist. int currentMark = 0; CurrentPoolUnuseCount.TryGetValue(element.Key,out currentMark); // be marked three times, add to garbage list. if (currentMark >= 3) { AddObjectsToGarbageList(ref opool.UnusingLinkedList); // count tick to reset. CurrentPoolUnuseCount[element.Key] = 0; clearGarbageFlag = true; // each time only gathing one pool to process. break; } } } } // leave time for mainthread to delete gameobjects. if (clearGarbageFlag) { icountLoopTime = 0; intervalTimeTodetect = 20f; Thread.Sleep(1000); timeCount = System.DateTime.Now; } // interval 20 seconds to start check once; if ((System.DateTime.Now - timeCount).TotalSeconds > intervalTimeTodetect) { timeCount = System.DateTime.Now; eachMinuteGetDicOnce = false; poolManagerTempDic.Clear(); icountLoopTime++; Debug.Log("Loop count is " + icountLoopTime); } // long time nothing happen, expand the detective interval time (max <= 90s). if (icountLoopTime >= 4 ) { intervalTimeTodetect = intervalTimeTodetect * 2 >= 90f ? 90f : intervalTimeTodetect * 2; icountLoopTime = 0; Debug.Log("interval time is " + intervalTimeTodetect); } } return; } private static void MarkCountForUnusingLink(ref Dictionary<int, int> poolUnuseCount,int prefabGuid) { Debug.Assert(null != poolManagerDic); int currentMark = 0; if (poolUnuseCount.ContainsKey(prefabGuid)) { poolUnuseCount.TryGetValue(prefabGuid, out currentMark); } currentMark++; if (poolUnuseCount.ContainsKey(prefabGuid)) { poolUnuseCount[prefabGuid] = currentMark; } else { poolUnuseCount.Add(prefabGuid, currentMark); } } /// <summary> /// add last gameobject to garbagelist,as when unsing unusinglink is from first place to get. /// </summary> /// <param name="list"></param> private static void AddObjectsToGarbageList(ref LinkedList<Transform> list) { Debug.Assert(list.Count > 0); int FlagDestroyNumber = list.Count>>1; for (int i = 0; i < FlagDestroyNumber; i++) { GarbageList.Add(list.Last.Value); list.RemoveLast(); } } public void Dispose() { isStartThread = false; } } }