先理解一下什么是线程:
线程是操作系统级别的概念,现代操作系统都实现并且支持线程,线程的调度对应用开发者是透明的,开发者无法预期某线程在何时被调度执行。基于此,一般那种随机出现的BUG,多与线程调度相关。
什么是Unity主线程?
通俗点讲,Unity的主流程生命周期函数就是主线程。
常用的生命周期函数:
Awake():唤醒事件,游戏一开始运行就执行,只执行一次。
OnEnable():启用事件,只执行一次。当脚本组件被启用的时候执行一次。
Start():开始事件,执行一次。
FixedUpdate():固定更新事件,执行N次,0.02秒执行一次。所有物理组件相关的更新都在这个事件中处理。
Update():更新事件,执行N次,每帧执行一次。
LateUpdate():稍后更新事件,执行N次,在 Update() 事件执行完毕后再执行。
OnGUI():GUI渲染事件,执行N次,执行的次数是 Update() 事件的两倍。
OnDisable():禁用事件,执行一次。在 OnDestroy() 事件前执行。或者当该脚本组件被“禁用”后,也会触发该事件。
OnDestroy():销毁事件,执行一次。当脚本所挂载的游戏物体被销毁时执行。
那么什么是子线程呢?(不在主线程运行的程序都叫子线程)
我的理解,最直接的是new Thread()一个线程
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
private Thread T;
void Start ()
{
T = new Thread(ThreadTest);
T.Start();
}
void ThreadTest()
{
for(int i = 0; i < 3; i++)
{
Debug.Log("子线程运行了");
}
}
}
还有就是编写网络通信时的回调函数也是子线程。
/// <summary>
/// 响应服务器,为回调函数
/// </summary>
/// <param name="data"></param>
public override void OnResponse(string data)
{
Debug.Log(data);
}
多线程一般在什么情况下使用呢?
大量耗时的数据计算
网络请求
复杂密集的I/O(文件写入读取)操作
为什么要使用多线程?
上面那三种情况,如果放到主线程,一般情况下都会卡的不要不要的,所以就需要子线程来分担工作压力
注意点:
-
协程不是子线程,对与Unity,它是单线程的设计,它更倾向使用time slicing(时间分片)的协程(coroutine)去完成异步任务,融合到了Unity生命周期中。
协程Coroutine是编译器级的,本质还是一个线程时间分片去执行代码段。它通过**相关的代码使得代码段能够实现分段式的执行,显式调用yield函数后才被挂起,重新开始的地方是yield关键字指定的,一次一定会跑到一个yield对应的地方。因为协程本质上还是在主线程里执行的,需要内部有一个类似栈的数据结构,当该coroutine被挂起时要保存该coroutine的数据现场以便恢复执行。
在Unity3D中,协程是可自行停止运行 (yield),直到给定的 YieldInstruction 结束再继续运行的函数。 协程 (Coroutines) 的不同用途:
• yield; 在下一帧上调用所有 Update 函数后,协同程序将继续运行。
• yield WaitForSeconds(2); 在指定的时间延迟之后,为此帧调用所有 Update 函数之后继续运行
• yield WaitForFixedUpdate(); 在所有脚本上调用所有 FixedUpdate 后继续运行
• yield WWW 完成 WWW 下载后继续运行。
• yield StartCoroutine(MyFunc); 连接协同程序,并等待 MyFunc coroutine 首先结束。
也就是说,将代码段分散在不同的帧中,每次执行一段,下一帧再执行yield挂起的地方。
-
Unity是单线程设计的游戏引擎,子线程中无法运行UnitySDK
例如,在子线程生成游戏物体(go已在外部添加引用)
using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { private Thread T; public GameObject go; void Start () { T = new Thread(ThreadTest); T.Start(); } void ThreadTest() { for(int i = 0; i < 3; i++) { Instantiate(go); Debug.Log("子线程运行了"); } } }
运行就会Internal_CloneSingle can only be called from the main thread.
在网络回调函数里生成游戏物体也会报同样的错误
本文可能略有不足,还请大家不吝赐教!
参考