首先说明unity多线程操作的适用范围:
- 网络请求
- 复杂密集的I/O操作
- 耗时的复杂算法计算(如网格动画)
unity多线程操作的限制:
- UnityEngine中的API对象不能在子线程中使用(如Unity的组件、对象和系统调用)
- UnityEngine总定义的基本数据结构可以使用(如vector/quaternion/float/int/struct可以使用)。
总的来说:对于不是画面更新,也不是常规的逻辑更新(指包括AI、物理碰撞、角色控制这些),而是一些其他后台任务,则可以将这个独立出来开辟一个子线程。以此来避免大计算量的运算将主线程卡死的情况。
基本函数
- Start()开始
- Abort()终止
- Join()阻塞
- Sleep()休眠;
using UnityEngine;
using System.Threading;
public class TreadTest : MonoBehaviour
{
private Thread t0;
private Thread t1;
private Thread t2;
static readonly private Object locker = new Object();
private int number = 0;
private void Start()
{
t0 = new Thread(TestFunc0);
t0.Start();
t1 = new Thread(TestFunc1);
t1.Start();
t2 = new Thread(TestFunc2);
t2.Start();
}
private void OnDestroy()
{
if (t0 != null)
t0.Abort();
if (t1 != null)
t1.Abort();
if (t2 != null)
t2.Abort();
}
private void TestFunc0()
{
lock (locker)
{
number += 1;
Debug.Log(number);
t0.Join(1000);
Thread.Sleep(1000);
}
}
private void TestFunc1()
{
lock (locker)
{
number += 20;
Debug.Log(number);
t1.Join(1000);
Thread.Sleep(1000);
}
}
private void TestFunc2()
{
lock (locker)
{
number += 300;
Debug.Log(number);
t2.Join(1000);
Thread.Sleep(1000);
}
}
}
lock关键字的作用:
lock事实上就是判断lock对象是否被锁:
- 如果没被锁,则往下运行代码,并锁上此锁。
- 如果被锁,则挂起该线程,解锁后按情况1处理。
达到的目的即:在多线程环境下,确保lock代码块中的对象只被一个线程操作,从而使得多线程对此对象是顺序访问。
lock关键字需要注意的地方有:
lock对象必须是一个不可变对象,否则无法阻止另一个线程进入临界区。最好是static readonly 或者static。
常见的lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 都是不合理的写法,其分别的原因是:
- lock(this),主要问题是如果类为实例化类,那么this也只是当前实例,不能起到锁定的作用,而且即使是静态类可以被锁定,那么由于尺寸问题,不如锁定不可变小对象合算。
- lock(typeof())问题在于锁定对象类型当相于锁定类型中的静态对象部分,锁定了所有实例,速度慢;另一方面,有可能程序其他部分在访问该对象,已经替您锁定,将导致您锁定语句的挂起。
- lock("")问题在于微软将内容相同的字符串视为同一个对象,如果您在此锁定了他,那么其实是锁定了同一个对象,将阻止其他地方对字符串的锁定。
实例函数join和静态函数 sleep达成的效果是一致的,皆是在非主线程的线程中调用,静态函数sleep即是挂起当前运行线程。经过试验,实例函数join的实例是哪个thread并不重要,皆是挂起当前运行线程,所以用静态函数sleep就好。
最后分享一篇说sleep说得非常好的文章