Unity-Loomのマルチスレッド研究と最適化

7805 ワード

1.Loomの原理
LoomはMonoBehaviourから継承され、Unityプロセス管理におけるUpdateメソッドの下でコールバックが必要なActionをチェックしてロックとコールバックを行い、メインスレッドで実行されることを確保し、コールバックシーケンス自体が静的データとして保存され、任意のスレッド呼び出しで追加され、簡単で軽量である
2.初期化最適化
原版Loomの初期化方法は煩雑で、各シーンを初期化する必要がありますが、実際にはUnityの非破壊方法を使用してLoomを永久物体として保存することができます.このように、初期化時のメインスレッドでLoom.Initialize();を1回呼び出した後、任意のシーンの任意のスレッドでLoom.QueueOnMainThread(()=>{
});
を呼び出してメインスレッドの動作を完了させるだけでよい
using UnityEngine;  
using System.Collections;  
using System.Collections.Generic;  
using System;  
using System.Threading;  
using System.Linq;  

public class Loom : MonoBehaviour  
{  
    public static int maxThreads = 8;  
    static int numThreads;

    private static Loom _current;
    public static Loom Current  
    {  
        get  
        {  
            Initialize();  
            return _current;  
        }  
    }  
    //####  Awake
//  void Awake()  
//  {  
//      _current = this;  
//      initialized = true;  
//  }  

    static bool initialized;  

    //####           ,             
    public static void Initialize()  
    {  
        if (!initialized)  
        {  

            if(!Application.isPlaying)  
                return;  
            initialized = true;  
            GameObject g = new GameObject("Loom");  
            //####    
            DontDestroyOnLoad (g);
            _current = g.AddComponent();  
        }  

    }  

    private List _actions = new List();  
    public struct DelayedQueueItem  
    {  
        public float time;  
        public Action action;  
    }  
    private List _delayed = new  List();  

    List _currentDelayed = new List();  

    public static void QueueOnMainThread(Action action)  
    {  
        QueueOnMainThread( action, 0f);  
    }  
    public static void QueueOnMainThread(Action action, float time)  
    {  
        if(time != 0)  
        {
            if (Current != null)
            {
                lock (Current._delayed)
                {
                    Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
                }
            }
        }  
        else  
        {
            if (Current != null)
            {
                lock (Current._actions)
                {
                    Current._actions.Add(action);
                }
            }
        }  
    }  

    public static Thread RunAsync(Action a)  
    {  
        Initialize();  
        while(numThreads >= maxThreads)  
        {  
            Thread.Sleep(1);  
        }  
        Interlocked.Increment(ref numThreads);  
        ThreadPool.QueueUserWorkItem(RunAction, a);  
        return null;  
    }  

    private static void RunAction(object action)  
    {  
        try  
        {  
            ((Action)action)();  
        }  
        catch  
        {  
        }  
        finally  
        {  
            Interlocked.Decrement(ref numThreads);  
        }  

    }  


    void OnDisable()  
    {  
        if (_current == this)  
        {  

            _current = null;  
        }  
    }  



    // Use this for initialization  
    void Start()  
    {  

    }  

    List _currentActions = new List();  

    // Update is called once per frame  
    void Update()  
    {  
        lock (_actions)  
        {  
            _currentActions.Clear();  
            _currentActions.AddRange(_actions);  
            _actions.Clear();  
        }  
        foreach(var a in _currentActions)  
        {  
            a();  
        }  
        lock(_delayed)  
        {  
            _currentDelayed.Clear();  
            _currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));  
            foreach(var item in _currentDelayed)  
                _delayed.Remove(item);  
        }  
        foreach(var delayed in _currentDelayed)  
        {  
            delayed.action();  
        }  



    }  
}