• simple_pool对象池——优化<二>



    本文章由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; } } }

  • 相关阅读:
    Netty之ProtoBuf(六)
    Netty对WebSocket的支持(五)
    Netty之心跳检测技术(四)
    Netty之多用户的聊天室(三)
    Docker Compose 笔记
    vue.js学习笔记
    powerdesigner 生成C#code 实体 模板设备
    .net 接收post 的参数 加载xml
    powerdesigner 生成实体代码 附加生成xml
    PostgreSql 获取所有的表、视图、字段、 主键
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7275419.html
Copyright © 2020-2023  润新知