(小白)unity多线程的学习:Loom-ThreadPoolScheduler类

来源:互联网 发布:淘宝店铺装修设计教程 编辑:程序博客网 时间:2024/05/29 16:25




using System;
using System.Collections;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using Frankfort.Threading.Internal;








//C# sharp








namespace Frankfort.Threading
{
    //声明委托对象
    public delegate void ThreadPoolSchedulerEvent(IThreadWorkerObject[] finishedObjects);
    public delegate void ThreadedWorkCompleteEvent(IThreadWorkerObject finishedObject);




    /// <summary>
    /// This is the most important class of the framework. It starts & stops threads, caps the amount of threads running at the same time, handles Mainthread completion-delegates, etc.
    /// 最重要的类,能够开启/停止线程,抓取同时运行的线程数,处理主线程委托机制
    /// </summary>
    public class ThreadPoolScheduler : MonoBehaviour
    {


        //--------------- public Session Variables 公有变量--------------------
        public bool DebugMode = false;//调试模式
        public bool ForceToMainThread = false;//是否将加入主线程
        public float WaitForSecondsTime = 0.001f; //等待时间
        
        //忙碌判断
        public bool isBusy
        {
            get
            {
                return _shedularBusy;
            }
        }
        
        //?
        public float Progress
        {
            get
            {
                if (workData == null || workData.workerPackages == null || workData.workerPackages.Length == 0)
                    return 1f;


                //return (float)GetHandledFinishedPackages().Length / (float)workData.workerPackages.Length;
                int finishedPackages = 0;
                int i = workData.workerPackages.Length;
                while(--i > -1)
                {
                    if (workData.workerPackages[i].finishedWorking)
                        finishedPackages++;
                }


                return (float)finishedPackages / (float)workData.workerPackages.Length;
            }
        }
        //--------------- public Session Variables --------------------








        //--------------- PrivateSession Variables  私有变量--------------------
        private bool _providerThreadBusy;//线程提供者忙碌状态
        private bool _shedularBusy;//调度器忙碌状态
        private bool _isAborted;//是否中止
        private ASyncThreadWorkData workData;//工作数据
        private Thread providerThread;//线程提供者
        private int workObjectIndex;//工作对象索引
        private ThreadPoolSchedulerEvent onCompleteCallBack;//线程池调度器事件 对象
        private ThreadedWorkCompleteEvent onWorkerObjectDoneCallBack;//线程工作完成事件 对象
        private bool safeMode;//安全模式
        //--------------- Private Session Variables --------------------
















        //--------------------------------------- UNITY MONOBEHAVIOUR COMMANDS  Unity方面控制--------------------------------------
        //--------------------------------------- UNITY MONOBEHAVIOUR COMMANDS --------------------------------------
        #region UNITY MONOBEHAVIOUR COMMANDS
        //初始化
        protected virtual void Awake()
        {
            MainThreadWatchdog.Init();//初始化  主线程监测器
            MainThreadDispatcher.Init();//初始化  主线程调度员
            UnityActivityWatchdog.Init();//初始化  Unity活动监测器
        }
        //应用是否退出
        protected virtual void OnApplicationQuit()
        {
            Debug.Log("ThreadPoolScheduler.OnApplicationQuit!");
            AbortASyncThreads();    
        }
        //销毁
        protected virtual void OnDestroy()
        {
            Debug.Log("ThreadPoolScheduler.OnDestroy!");
            AbortASyncThreads();//调用同一个方法,但原因不一样
        }
        #endregion
        //--------------------------------------- UNITY MONOBEHAVIOUR COMMANDS --------------------------------------
        //--------------------------------------- UNITY MONOBEHAVIOUR COMMANDS --------------------------------------
            
























        //--------------------------------------- UNITY COROUTINE & PROVIDER-THREAD IMPLEMENTATION  Unity协程 和 线程提供者 完善--------------------------------------
        //--------------------------------------- UNITY COROUTINE & PROVIDER-THREAD IMPLEMENTATION --------------------------------------
        #region UNITY COROUTINE & PROVIDER-THREAD IMPLEMENTATION


        /// <summary>
        /// Unlike "StartMultithreadedWorkloadExecution", you will have to build your own IThreadWorkerObject.
        /// 自己创建线程工作者对象
        /// Downside: It requires some extra work. Upside: you got more controll over what goes in and comes out
        /// 缺点:额外工作  优点:控制大
        /// Infact: You can create you own polymorphed IThreadWorkerObject-array, each ellement being a completely different type. For example: the statemachines of enemies are IThreadWorkerObject's and the array contains completely different classes with enemies/AI-behaviours.
        /// </summary>
        /// <param name="workerObjects">An array of IThreadWorkerObject objects to be handled by the threads. If you want multiple cores/threads to be active, make sure that the number of IThreadWorkerObject's proves matches/exeeds your preferred number maxWorkingThreads. </param>
        /// <param name="onComplete">Fired when all re-packaged workLoad-objects are finished computing</param>
        /// <param name="onPackageExecuted">Fires foreach finished re-packaged set of workLoad-object</param>
        /// <param name="maxThreads"> Lets you choose how many threads will be run simultaneously by the threadpool. Default: -1 == number of cores minus one, to make sure the MainThread has at least one core to run on. (quadcore == 1 core Mainthread, 3 cores used by the ThreadPoolScheduler)</param>
        /// <param name="scheduler">If Null, a new ThreadPoolScheduler will be instantiated.</param>
        /// <param name="safeMode">Executes all the computations within try-catch events, logging it the message + stacktrace</param>
        public void StartASyncThreads(IThreadWorkerObject[] workerObjects, ThreadPoolSchedulerEvent onCompleteCallBack, ThreadedWorkCompleteEvent onPackageExecuted = null, int maxThreads = -1, bool safeMode = true)
        {
            if (_shedularBusy)//若调度器忙碌
            {
                Debug.LogError("You are trying to start a new ASync threading-process, but is still Busy!");
                return;
            }


            if (workerObjects == null || workerObjects.Length == 0)//若工作对象数组为0或空
            {
                Debug.LogError("Please provide an Array with atleast \"IThreadWorkerObject\"-object!");
                return;
            }


            //设置相关字段
            _isAborted = false;//
            _shedularBusy = true;
            _providerThreadBusy = true;
            this.onCompleteCallBack = onCompleteCallBack;
            this.onWorkerObjectDoneCallBack = onPackageExecuted;
                
            if (!ForceToMainThread)
            {   
                //--------------- Start Waiting for the Provider-thread to complete --------------------
                StartCoroutine("WaitForCompletion");
                workData = new ASyncThreadWorkData(workerObjects, safeMode, maxThreads);
                providerThread = new Thread(new ThreadStart(InvokeASyncThreadPoolWork));
                providerThread.Start();
                //--------------- Start Waiting for the Provider-thread to complete --------------------
            }
            else
            {
                //--------------- Execute all work in one bunch! --------------------
                StartCoroutine(WaitAndExecuteWorkerObjects(workerObjects));
                //--------------- Execute all work in one bunch! --------------------
            }
        }








        private IEnumerator WaitAndExecuteWorkerObjects(IThreadWorkerObject[] workerObjects)
        {
            yield return new WaitForEndOfFrame();
            for (int i = 0; i < workerObjects.Length; i++)
            {
                workerObjects[i].ExecuteThreadedWork();


                if (onWorkerObjectDoneCallBack != null)
                    onWorkerObjectDoneCallBack(workerObjects[i]);
            }


            _shedularBusy = false;
            _providerThreadBusy = false;


            if (onCompleteCallBack != null)
                onCompleteCallBack(workerObjects);
        }








        private IEnumerator WaitForCompletion()
        {
            if (DebugMode)
                Debug.Log(" ----- WaitForCompletion: " + Thread.CurrentThread.ManagedThreadId);
            
            while (!_isAborted)
            {
                //After waiting a while, in the meantime it might have finished itself, or got aborted!
                yield return new WaitForSeconds(WaitForSecondsTime);


                if(_isAborted)
                    break;


                //--------------- fire events while still working --------------------
                int finishedObjectsCount = GetFinishedPackagesCount();
                if (finishedObjectsCount == workData.workerPackages.Length)
                    break;
        
                int unhandledPackagesCount = GetUnhandledFinishedPackagesCount();
                if (DebugMode)
                    Debug.Log(" ----- unhandledPackages: " + unhandledPackagesCount + " ( out of: " + finishedObjectsCount + " completed so far...)");


                if (unhandledPackagesCount > 0)
                {
                    foreach (ThreadWorkStatePackage package in workData.workerPackages)
                    {
                        if (package.finishedWorking && !package.eventFired)
                        {
                            if (onWorkerObjectDoneCallBack != null)
                                onWorkerObjectDoneCallBack(package.workerObject);


                            package.eventFired = true;
                        }
                    }
                }
                //--------------- fire events while still working --------------------
            }


            if(!_isAborted)
            {
                if (DebugMode)
                    Debug.Log(" ----- Coroutine knows its done!");


                IThreadWorkerObject[] workedObjects = GetWorkerObjectsFromPackages();
                
                workData.Dispose();
                workData = null;
                _shedularBusy = false;
                
                if (onCompleteCallBack != null)
                    onCompleteCallBack(workedObjects);
            }
        }
        
        #endregion
        //--------------------------------------- UNITY COROUTINE & PROVIDER-THREAD IMPLEMENTATION --------------------------------------
        //--------------------------------------- UNITY COROUTINE & PROVIDER-THREAD IMPLEMENTATION --------------------------------------






















        //--------------------------------------- EXTRA COMMANDS & ACTIONS 额外控制 和 响应动作 --------------------------------------
        //--------------------------------------- EXTRA COMMANDS & ACTIONS --------------------------------------
        #region EXTRA COMMANDS & ACTIONS


        /// <summary>
        /// Aborts all worker processes currently queued.
        /// 停止当前所有进程队列
        /// </summary>
        /// <param name="sleepTillAborted">if true: Makes sure that after invoking "AbortASyncThreads" the ThreadPoolSheduler is available again, but halts the MainThread while waiting for the other threads to finish</param>
        public void AbortASyncThreads()
        {
            if (!_providerThreadBusy)
                return;


            _isAborted = true;
            StopCoroutine("WaitForCompletion");


            if(workData != null && workData.workerPackages != null)
            {
                lock (workData.workerPackages)
                {
                    foreach (ThreadWorkStatePackage package in workData.workerPackages)
                    {
                        if (package.running && !package.finishedWorking)
                            package.workerObject.AbortThreadedWork();
                    }
                }
            }




            if (providerThread != null && providerThread.IsAlive)
            {
                Debug.Log("ThreadPoolScheduler.AbortASyncThreads - Interrupt!");
                providerThread.Interrupt();
                providerThread.Join();
            }
            else
            {
                Debug.Log("ThreadPoolScheduler.AbortASyncThreads!");
            }


            _providerThreadBusy = false;
        }
        
        #endregion
        //--------------------------------------- EXTRA COMMANDS & ACTIONS --------------------------------------
        //--------------------------------------- EXTRA COMMANDS & ACTIONS --------------------------------------
            






        












        //--------------------------------------- .NET THREADPOOL IMPLEMENTATION  实施线程池--------------------------------------
        //--------------------------------------- .NET THREADPOOL IMPLEMENTATION --------------------------------------
        #region .NET THREADPOOL IMPLEMENTATION


        /// <summary>
        /// This method is the work-provider-method. It makes sure the .NET threadpool has things to do...
        /// 供应者相关方法:确保线程池不空
        /// This method is NOT invoked by the mainThread, therefor is safe to use WaitHandle.WaitAll / WaitAny without halting Unity's gameThread!
        /// 此方法不由主线程调用,因此即使使用wait也不影响Unity的游戏线程
        /// 唤醒AsyncThreadPoolWork
        /// </summary>
        public void InvokeASyncThreadPoolWork()
        {
            UnityActivityWatchdog.SleepOrAbortIfUnityInactive();


            int totalWork = workData.workerPackages.Length;
            int startBurst = Mathf.Clamp(workData.maxWorkingThreads, 1, totalWork);


            if (DebugMode)
                Debug.Log(" ----- InvokeASyncThreadPoolWork. startBurst: " + startBurst + ", totalWork: " + totalWork);
            
            //--------------- Initial Startup burst --------------------
            for (int i = 0; i < startBurst && !_isAborted; i++)
            {
                //Add to .NET ThreadPool
                if (workData.workerPackages[i] != null)
                {
                    workData.workerPackages[i].started = true;
                    ThreadPool.QueueUserWorkItem(workData.workerPackages[i].ExecuteThreadWork, i);
                }
            }
            //--------------- Initial Startup burst ---------------s-----




            if (DebugMode)
                Debug.Log(" ----- Burst with WorkerObjects being processed!");


            //--------------- Create a new Thread to keep the Threadpool running  & cores saturated! --------------------
            //创建一个新线程保持线程池运行  和  饱和??
            workObjectIndex = startBurst; //at this point the amount of running WorkObjects/Threads is equal to the startBurst;
            while (workObjectIndex < totalWork && !_isAborted)
            {
                UnityActivityWatchdog.SleepOrAbortIfUnityInactive();


                AutoResetEvent[] startedEvents = GetStartedPackageEvents();
                if (startedEvents.Length > 0)
                    WaitHandle.WaitAny(startedEvents);
                
                workData.workerPackages[workObjectIndex].started = true;
                ThreadPool.QueueUserWorkItem(workData.workerPackages[workObjectIndex].ExecuteThreadWork, workObjectIndex);
                workObjectIndex++;
            } 
            //--------------- Create a new Thread to keep the Threadpool running & cores saturated! --------------------




            if (DebugMode)
                Debug.Log(" ----- all packages fed to the pool!");
            
            //--------------- Wait till all are finished! --------------------
            //All WorkObjects have been set to work, but the last few Threads might still be pending!
            AutoResetEvent[] pendingEvents = GetStartedPackageEvents();
            if (pendingEvents.Length > 0)
            {
                UnityActivityWatchdog.SleepOrAbortIfUnityInactive();
                WaitHandle.WaitAll(pendingEvents);
            }
            //--------------- Wait till all are finished! --------------------




            if (DebugMode)
                Debug.Log(" ----- PROVIDER THREAD DONE");
    
            //DONE!
            _providerThreadBusy = false;
        }


        #endregion
        //--------------------------------------- .NET THREADPOOL IMPLEMENTATION --------------------------------------
        //--------------------------------------- .NET THREADPOOL IMPLEMENTATION --------------------------------------


























        //--------------------------------------- MISC  其他指令方法--------------------------------------
        //--------------------------------------- MISC --------------------------------------
        #region MISC


        private AutoResetEvent[] GetStartedPackageEvents()
        {
            List<AutoResetEvent> result = new List<AutoResetEvent>();
            for (int i = 0; i < workData.workerPackages.Length; i++)
            {
                ThreadWorkStatePackage package = workData.workerPackages[i];
                if(package.started && !package.finishedWorking)
                    result.Add(package.waitHandle);
            }
            return result.ToArray();
        }


        
        private IThreadWorkerObject[] GetWorkerObjectsFromPackages()
        {
            if (workData == null || workData.workerPackages == null)
                return new IThreadWorkerObject[0];


            IThreadWorkerObject[] result = new IThreadWorkerObject[workData.workerPackages.Length];
            int i = workData.workerPackages.Length;
            while (--i > -1)
                result[i] = workData.workerPackages[i].workerObject;


            return result;
        }


        public int GetFinishedPackagesCount()
        {
            if (workData == null || workData.workerPackages == null)
                return 0;


            int count = 0;
            int i = workData.workerPackages.Length;
            while (--i > -1)
            {
                if (workData.workerPackages[i].finishedWorking)
                    count++;
            }
            return count;
        }


        public int GetUnhandledFinishedPackagesCount()
        {
            if (workData == null || workData.workerPackages == null)
                return 0;
            
            int count = 0;
            int i = workData.workerPackages.Length;
            while (--i > -1)
            {
                if (workData.workerPackages[i].finishedWorking && !workData.workerPackages[i].eventFired)
                    count++;
            }
            return count;
        }


        #endregion
        //--------------------------------------- MISC --------------------------------------
        //--------------------------------------- MISC --------------------------------------




    }
}
0 0