前言
因爲項目後期,好久沒有更博了。最近項目推上線了,突然閒下來了。想寫的東西很多。就先從多線程開始吧。
個人對線程的理解還不夠透徹,希望如果有更好的看法可以在評論區提出,感謝。
什麼多線程
在一個程序中,一些獨立運行的程序片段叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理”(Multithreading)。
在Unity中使用多線程能幹什麼?
使用Unity開發的應用,只能在主線程中訪問Unity組件,這一點限制了部分的優化。不過小夥伴可以開一些輔助線程。
允許開線程來輔助的功能業務
- 大量的數據計算 (eg. A*尋路算法之類的)
- IO操作
- 解壓縮資源
- 不涉及Unity組件的資源加載(Ps.關於涉及Unity組件的加載可以通過邏輯剝離來實現,這邊只拋出一個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<Action> _actions = new List<Action>();
private List<Action> _currentActions = new List<Action>();
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
#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<Loom>();
}
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會有16B的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();
}
}