Unity-Loom的多线程研究及优化

1.Loom的原理

Loom继承自MonoBehaviour,在Unity流程管理中Update方法下检查需要回调的Action进行加锁并回调,确保在主线程执行,回调序列本身又作为静态数据保存,在任意线程调用添加,简单轻量

2.初始化优化

原版Loom的初始化方法较为繁琐,每个场景都需要进行初始化,其实可以使用Unity的永不销毁方法来将Loom作为一个永久物体保存下来。
这样,只需在初始化时的主线程中调用一次
Loom.Initialize();
之后在任意场景任意线程均可调用
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<Loom>(); 
    } 

  } 

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

  List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>(); 

  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<Action> _currentActions = new List<Action>(); 

  // 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(); 
    } 



  } 
}