了解Unity中的多线程及使用多线程

如果你想在游戏中使用多线程,你应该看看这篇文章,线程是一个相当复杂的话题,但如果你掌握了它,你就可以从容的使用多个硬件处理器或处理很难划分管理数据块.

如在场景中用A*算法进行大量的数据计算.
变形网格中操作大量的顶点.
持续的要运行上传数据到服务器.
二维码识别等图像处理.

如果同时你要处理很多事情或者与Unity的对象互动小可以用thread,否则使用coroutine.

线程是在你程序中与其他线程同时运行的进行.在多处理器的计算机上可以做到多个线程的真正的同步.更多的线程取决于有多个处理核心.

Unity编程时,总有个主线程执行你的代码,也可以创建额外的线程和主线程同时运行.

而Unity中,你仅能从主线程中访问Unity的组件,对象和Unity系统调用.任何企图访问这些项目的第二个线程都将失败并引发错误.这是一个要重视的一个限制.

所以当你写代码时,你认为一个函数开始并达到它执行的点后返回,同样你做的东西又在另外一个函数中执行,但又没有发生相应的变化.操作系统决定你代码的执行,任何时候,你的代码只能暂时”休眠”掉,然后让另外的代码开始运行,

在这个例子中,在第一个线程将A的值加载到CPU寄存器中准备+1后被中断,第二个线程来读取A的值,并减去1000,这时A应该是-950.现在第一个线程重新开始,它在寄存器中的50+1的结果存储于A,A变成了51,而-950已经丢掉了.

从根本上说,要在用多个线程在同时对变量或内存访问时,要采取很多预防措施来确保不会发生这样的事.

所以Unity决定从另外线程访问这些变量或者内存是无效的,只是为了避免所有系统和框架对象出现问题.

所以要确保一次只有一个线程来修改变量,这不意味着你不能用多线程工作,你可以用”排序”来解决这个问题.

C#中有lock这个关键字,以确保只有一个线程可以在特定时间内访问特定的对象.这里说对象是因为无法锁定一个类型值(value type)或原型(primitive).

int a = 50;

object guard = new object();

void ThreadOneCode()
{
//一些代码在这

lock(guard)
{
a = a + 1;
}

//其余一些代码在这

}

void ThreadTwoCode()
{
//一些代码在这

lock(guard)
{
a = a - 1000;
}

//其余一些代码在这
}

所有都锁定在guard内,保证同一个时间只有一个线程通过guard访问它.你可以使用任何合适的对象.

现在你可能会有各种各样的问题,比如你要锁定的不止一件事,可能是互相嵌套的.那我们该怎么办呢?

我们这个类叫Loom,让你可以轻松在另一个线程运行代码,

这里有两个要注意的功能:

RunAsync(Action)-在另一个线程上运行的一组代码.
QueueOnMainThread(Action,[可选]float time)-运行在主线程的语句(可选延迟).

用Loom.Current访问Loom-创建一个看不见的GameObject用来处理游戏主线程的互动.

下面这个例子用Loom来更新一个网格所有的顶点乘的结果.

//缩放一个网格在第二个线程
void ScaleMesh(Mesh mesh, float scale)
{
//Get the vertices of a mesh
var vertices = mesh.vertices;
//运行一个Action在新的线程
Loom.RunAsync(()=>{
//遍历所有的顶点
for(var i = 0; i < vertices.Length; i++)
{
//缩放顶点
vertices[i] = vertices[i] * scale;
}
//在主线程上运行一些代码
//更新网格
Loom.QueueOnMainThread(()=>{
//设置顶点
mesh.vertices = vertices;
//重新计算边界
mesh.RecalculateBounds();
});

上面这个是个很好的例子,使用lambda函数在第二个线程上做一个没有参数,不需要返回任何内容的操作. closures都是在你自己的类和函数的参数和局部变量的访问.

你可以用 ()=>{ … } 定义一个lambda函数来在新的线程上运行函数内所有的代码.

在主线程上我们需要将修改的网格顶点更新,所以我们使用QueueOnMainThread在接下来的时间更新周期运行处理(此帧或下一帧被称为接下来的更新周期). QueueOnMainThread也需要一个Action来将更新的顶点更新到原来的网格.


如果是UnityScript,你可以这样使用Loom:

//缩放一个网格在第二个线程
function ScaleMesh(mesh : Mesh, scale : float)
{
//Get the vertices of a mesh
var vertices = mesh.vertices;
//运行一个Action在新的线程
Loom.RunAsync(function() {
//遍历所有的顶点
for(var i = 0; i < vertices.Length; i++)
{
//缩放顶点
vertices[i] = vertices[i] * scale;
}
//在主线程上运行一些代码
//更新网格
Loom.QueueOnMainThread(function() {
//设置顶点
mesh.vertices = vertices;
//重新计算边界
mesh.RecalculateBounds();
});

原文:http://unitygems.com/threads/

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity使用多线程可以提高游戏的性能和响应速度。但需要注意的是,Unity的所有API都是线程不安全的,所以在使用多线程时,必须采用正确的方式来访问Unity API。 以下是在Unity使用多线程的步骤: 1. 创建新的线程 使用C#的Thread类创建新的线程,如下所示: ``` Thread thread = new Thread(ThreadMethod); thread.Start(); ``` 其,`ThreadMethod`是新线程要执行的方法。 2. 在新线程执行逻辑 在新线程执行复杂的计算或其他需要耗费时间的操作。需要注意的是,在新线程不能直接访问Unity API。 3. 使用线程安全的方式访问Unity API 为了避免访问Unity API时出现线程安全问题,可以使用以下方法: - 使用线程安全的类型,如ConcurrentQueue,来存储需要在主线程处理的数据。 - 使用Unity的主线程调用方法,如`UnityMainThreadDispatcher.Instance().Enqueue()`方法,将需要在主线程执行的代码添加到主线程的执行队列。 以下是一个使用多线程的示例代码: ``` private ConcurrentQueue<float> queue = new ConcurrentQueue<float>(); private void Update() { float value; while (queue.TryDequeue(out value)) { // 在主线程处理数据 Debug.Log(value); } } private void ThreadMethod() { for (float i = 0; i < 10000; i++) { queue.Enqueue(i); } } ``` 在上面的示例代码,我们使用ConcurrentQueue存储需要在主线程处理的数据。然后在Update方法不断地尝试从队列取出数据并在主线程处理。在新线程,我们向队列添加数据。这样可以保证在主线程处理数据,避免了访问Unity API时出现线程安全问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值