Unity如何使用多线程处理 UnityEngine相关的内容
2016-12-07 14:55
951 查看
介绍一个插件Loom可以让你在Unity使用多线程处理UnityEngine的逻辑(包含Unity的API)。
Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变量,如Vector3等。这样就使得线程在Unity中显的很鸡肋和蹩脚,因为很多函数很都是UnityEngine类或函数的调用的,对于哪些是可以在多线程使用,如下总结:
1. 变量(都能指向相同的内存地址)都是共享的
2. 不是UnityEngine的API能在分线程运行
3. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。
4. UnityEngine定义的基本类型的函数可以在分线程运行,如
int i = 99;
print (i.ToString());
Vector3 x = new Vector3(0,0,9);
x.Normalize();
类的函数不能在分线程运行
obj.name
实际是get_name函数,分线程报错误:get_name can only be called from the main thread.
Texture2D tt = new Texture2D(10,10);
实际会调用UnityEngine里的Internal_Create,分线程报错误:Internal_Create can only be called from the main thread.
其他transform.position,Texture.Apply()等等都不能在分线程里运行。
结论: 分线程可以做 基本类型的计算, 以及非Unity(包括.Net及SDK)的API。
我觉得Unity做了这个限制,主要是Unity的函数执行机制是帧序列调用,甚至连Unity的协程Coroutine的执行机制都是确定的,如果可以使用多线程访问UnityEngine的对象和api就得考虑同步问题了,也就是说Unity其实根本没有多线程的机制,协程只是达到一个延时或者是当指定条件满足是才继续执行的机制。
以下是插件的介绍:
Our class is called Loom. Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
There are only two functions to worry about:
RunAsync(Action) which runs a set of statements on another thread
QueueOnMainThread(Action, [optional] float time) - which runs a set of statements on the main thread (with an optional delay).
You access Loom using Loom.Current - it deals with creating an invisible game object to interact with the games main thread.
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 = 8;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
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();
}
}
}
RunAsync(Action)和QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在C#线程和Unity的主线程中交叉运行。原理也很简单:用线程池去运行RunAsync(Action)的函数,在Update中运行QueueOnMainThread(Acition, [optional]
float time)传入的函数。
有了Loom这个工具类,在很多涉及UnityEngine对象的耗时计算还是可以得到一个解决方法的:
如在场景中用A*算法进行大量的数据计算
变形网格中操作大量的顶点
持续的要运行上传数据到服务器
二维码识别等图像处理
Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变量,如Vector3等。这样就使得线程在Unity中显的很鸡肋和蹩脚,因为很多函数很都是UnityEngine类或函数的调用的,对于哪些是可以在多线程使用,如下总结:
1. 变量(都能指向相同的内存地址)都是共享的
2. 不是UnityEngine的API能在分线程运行
3. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。
4. UnityEngine定义的基本类型的函数可以在分线程运行,如
int i = 99;
print (i.ToString());
Vector3 x = new Vector3(0,0,9);
x.Normalize();
类的函数不能在分线程运行
obj.name
实际是get_name函数,分线程报错误:get_name can only be called from the main thread.
Texture2D tt = new Texture2D(10,10);
实际会调用UnityEngine里的Internal_Create,分线程报错误:Internal_Create can only be called from the main thread.
其他transform.position,Texture.Apply()等等都不能在分线程里运行。
结论: 分线程可以做 基本类型的计算, 以及非Unity(包括.Net及SDK)的API。
我觉得Unity做了这个限制,主要是Unity的函数执行机制是帧序列调用,甚至连Unity的协程Coroutine的执行机制都是确定的,如果可以使用多线程访问UnityEngine的对象和api就得考虑同步问题了,也就是说Unity其实根本没有多线程的机制,协程只是达到一个延时或者是当指定条件满足是才继续执行的机制。
以下是插件的介绍:
Threads on a Loom
Our class is called Loom. Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.There are only two functions to worry about:
RunAsync(Action) which runs a set of statements on another thread
QueueOnMainThread(Action, [optional] float time) - which runs a set of statements on the main thread (with an optional delay).
You access Loom using Loom.Current - it deals with creating an invisible game object to interact with the games main thread.
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 = 8;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
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();
}
}
}
RunAsync(Action)和QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在C#线程和Unity的主线程中交叉运行。原理也很简单:用线程池去运行RunAsync(Action)的函数,在Update中运行QueueOnMainThread(Acition, [optional]
float time)传入的函数。
//Scale a mesh on a second thread void ScaleMesh(Mesh mesh, float scale) { //Get the vertices of a mesh var vertices = mesh.vertices; //Run the action on a new thread Loom.RunAsync(()=>{ //Loop through the vertices for(var i = 0; i < vertices.Length; i++) { //Scale the vertex vertices[i] = vertices[i] * scale; } //Run some code on the main thread //to update the mesh Loom.QueueOnMainThread(()=>{ //Set the vertices mesh.vertices = vertices; //Recalculate the bounds mesh.RecalculateBounds(); }); }); }
有了Loom这个工具类,在很多涉及UnityEngine对象的耗时计算还是可以得到一个解决方法的:
如在场景中用A*算法进行大量的数据计算
变形网格中操作大量的顶点
持续的要运行上传数据到服务器
二维码识别等图像处理
相关文章推荐
- 多线程补充、多线程问题及处理、线程的同步及相关内容
- 黄聪:C#里如何使用WebBrowser获取处理AJAX生成的网页内容?
- linux下文件的读写操作、字符串的处理操作,多线程操作等相关函数使用说明
- Visual Studio 2017中使用正则修改部分内容 如何使用ILAsm与ILDasm修改.Net exe(dll)文件 C#学习-图解教程(1):格式化数字字符串 小程序开发之图片转Base64(C#、.Net) jquery遍历table为每一个单元格取值及赋值 。net加密解密相关方法 .net关于坐标之间一些简单操作
- 黄聪:C#里如何使用WebBrowser获取处理AJAX生成的网页内容?
- 如何使用rel="canonical"来处理重复内容
- 如何在C++类中安全地使用多线程中的处理函数
- Unity的协程,多线程,处理与使用方法
- Spring-Boot中如何使用多线程处理任务
- Spring-Boot中如何使用多线程处理任务
- 如何在asp.net中使用多线程及队列,异步处理一个耗时的任务(原创)
- 使用OpenMP进行多线程处理时,如何退出多线程
- 使用GCD处理非UI相关的异步任务 Object-C异步多线程加载网络图片
- Spring-Boot中如何使用多线程处理任务
- Windows 窗体控件中的多线程处理之:如何使用后台线程搜索文件
- 多线程处理:如何使用同步类
- 详解Spring-Boot中如何使用多线程处理任务
- 如何设置一个style做一个属性触发去改变其他的属性值<style的相关内容>?
- 如何使用C#在发送往client的内容上加js代码
- 如何使用Transact-SQL进行事务处理[示例]