首先Unity一般是避免使用多线程的,unity提供了一种协程的概念(coroutine) yield,但是这个协程归根到底也不是多线程,它只是起到一个延迟执行的效果。
但是为什么我们需要使用多线程呢?前段时间项目中有一些地方使用协程并不能达到很好的效果,比如在游戏内进行大批量的拷贝和移动文件时,UI显示上会出现卡死现象,我怀疑是主线程被阻塞了。换成多线程后成功解决了问题。
适合使用多线程的地方
·解压缩资源
·IO操作
·网络请求
·大量的数据操作
·一些资源的加载
使用多线程是请注意
·Unity API 无法在Thread中被调用,同时包括所有component
· 变量都是共享的(都能指向相同的内存地址)
· UnityEngine 定义的基本结构(int, float, struct 定义的数据类型)可以在分线程计算,如 Vector3(struct)可以, 但 Texture2d(class,根父类为 Object) 不可以
如何使用多线程
多线程使用时必须非常谨慎,而unity自带的Thread,并不是mono而是.net,是两套框架。therad的使用晦涩复杂,同时还需要注意线程安全问题。
所以我选择了一个市面上封装比较好的Loom工具类,代码很少,将会在最后贴出来,非常好用。
//RunAsync 用线程池,运行在子线程。
public static Thread RunAsync(Action a)
//主线程
public static void QueueOnMainThread(Action action)
public static void QueueOnMainThread(Action action, float time)
举个栗子
Loom.RunAsync(delegate {
do something
});
全部代码
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 = 10;
public static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
public static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying) {
return;
}
initialized = true;
var g = new GameObject("Loom");
_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)
{
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 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();
}
}
}