Unityにおけるマルチスレッドの設計と実現


  • 前言
  • マルチスレッド
  • Unityでマルチスレッドを使用して何ができるか
  • 栗を持ち上げる時間は
  • になりました
  • 最後の
  • に書いてあります

    前言
    プロジェクトの後期のため、久しぶりにもっと博しました.最近プロジェクトがオンラインになったので,急に暇になった.書きたいことが多い.まずマルチスレッドから始めましょう.個人のスレッドに対する理解はまだ十分ではありません.もっと良い見方があれば、コメントエリアで提出してほしいです.ありがとうございます.
    マルチスレッド
    1つのプログラムでは、独立して実行されるプログラムの一部を「スレッド」(Thread)と呼び、それを利用してプログラミングされる概念を「マルチスレッド処理」(Multithreading)と呼ぶ.
    Unityでマルチスレッドを使うと何ができますか?
  • Unityが開発したアプリケーションを使用して、メインスレッドでのみUnityコンポーネントにアクセスできる点は、一部の最適化を制限しています.しかし、パートナーはいくつかの補助スレッドを開くことができます.
  • は、スレッドを開いて支援する機能トラフィックを許可する
  • 大量のデータ計算(eg.A*ルックアップアルゴリズムなど)
  • IO動作
  • リソースの解凍
  • はUnityコンポーネントのリソースロードに関与しない(Ps.Unityコンポーネントに関するロードは論理剥離によって実現でき、ここでは1つのideaだけを投げ出して発散させる.)
  • .

    栗を挙げる時間です
  • まずMonoBehaviourを継承します.
  • /*---------------------------------------------------------------- // Copyright © 2016 Jhon // // FileName: Loom.cs // Describle: // Created By: Jhon // Date&Time: 2016 10 14  17:31:26 // Modify History: // //--------------------------------------------------------------*/
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System;
    using System.Threading;
    
    public class Loom : MonoBehaviour
    {
    
    }
    
  • スレッド処理のItem構造と変数の定義を定義します.
  • #region StructMember
        public struct DelayedQueueItem
        {
            public float time;
            public Action action;
        }
    #endregion
    
    #region Member
        public static int mMaxThreads = 8;
    
        private static Loom _current;
        public static Loom Current
        {
            get
            {
                Initialize();
                return _current;
            }
        }
    
        private static int mNumThreads;
        private static bool initialized;
    
        private int _count;
        private List _actions = new List();
        private List _currentActions = new List();
        private List _delayed = new List();
        private List _currentDelayed = new List();
    
    #endregion
    
  • MonoBehaviourのライフサイクルを使用してビジネスロジック
  • を処理
    #region MonoLife
    
        void Awake()
        {
            _current = this;
            initialized = true;
        }
    
        void OnEnable()
        {
            if (null == _current)
            {
                _current = this;
            }
        }
    
        void OnDisable()
        {
            if (this == _current)
            {
                _current = null;
            }
        }
    
        void Update()
        {
            lock (_actions)
            {
                _currentActions.Clear();
                _currentActions.AddRange(_actions);
                _actions.Clear();
            }
    
            for (int i = 0;i < _currentActions.Count;i++)
            {
                _currentActions[i]();
            }
    
            lock (_delayed)
            {
                _currentDelayed.Clear();
    
                for(int i = _delayed.Count - 1;i > 0;i--)
                {
                    if(_delayed[i].time > Time.time)
                    {
                        continue;
                    }
    
                    _currentDelayed.Add(_delayed[i]);
                    _delayed.RemoveAt(i);
    
                }
            }
    
            for(int i = 0;i < _currentDelayed.Count;i++)
            {
                _currentDelayed[i].action();
            }
    
        }
    
    #endregion
  • ビジネスロジック
  • #region BusinessLogic
    
        private static void Initialize()
        {
            if (initialized == true)return;
            if (Application.isPlaying == false)return;
    
            initialized = true;
            var g = new GameObject("Loom");
            _current = g.AddComponent();
    
        }
    
        private static void RunAction(object action)
        {
            try
            {
                ((Action)action)();
            }
            catch(Exception e)
            {
                Log.Error(e.Message);
            }
            finally
            {
                Interlocked.Decrement(ref mNumThreads);
            }
        }
    
    #endregion
  • 単例モード対外提供API
  • #region PublicTools
        public static void QueueOnMainThread(Action action, float time = 0.0f)
        {
            if (time != 0)
            {
                lock (Current._delayed)
                {
                    Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
                }
            }
            else
            {
                lock (Current._actions)
                {
                    Current._actions.Add(action);
                }
            }
        }
    
        public static Thread RunAsync(Action varAction)
        {
            Initialize();
            while (mNumThreads >= mMaxThreads)
            {
                Thread.Sleep(1);
            }
            Interlocked.Increment(ref mNumThreads);
            ThreadPool.QueueUserWorkItem(RunAction, varAction);
            return null;
        }
    #endregion

    最後に書く
    Update関数ではforeachを使って遍歴できますが、私の別のブログではforeachを使うと16 BのGCがあると言いました.Updateでの操作であることを考慮して、forを使用して遍歴することを推奨します.以下にforeachバージョンを貼ります.
        private void OldVersionUpdate()
        {
            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();
            }
        }