`

Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享

 
阅读更多

Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享

By D.S.Qiu

尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com

 

         熟悉Unity的developer都知道在Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变量,如Vector3等。这样就使得线程在Unity中显的很鸡肋和蹩脚,因为很多函数很都是UnityEngine类或函数的调用的,对于哪些是可以在多线程使用,风雨冲进行了如下总结:

 

0. 变量(都能指向相同的内存地址)都是共享的

1. 不是UnityEngine的API能在分线程运行

2. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。

3. 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。

        D.S.Qiu觉得Unity做了这个限制,主要是Unity的函数执行机制是帧序列调用,甚至连Unity的协程Coroutine的执行机制都是确定的,如果可以使用多线程访问UnityEngine的对象和api就得考虑同步问题了,也就是说Unity其实根本没有多线程的机制,协程只是达到一个延时或者是当指定条件满足是才继续执行的机制。

        我们的项目目前还有没有比较耗时的计算,所以还没有看到Thread的使用。本来一直没有太考虑着方面的事情,直到在UnityGems.com看到Loom这个类,叹为观止呀。直接贴出人家的介绍(没必要翻译了大笑):

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.

        

        我们只需要关系两个函数:RunAsync(Action)和QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在C#线程和Unity的主线程中交叉运行。原理也很简单:用线程池去运行RunAsync(Action)的函数,在Update中运行QueueOnMainThread(Acition, [optional] float time)传入的函数。

 

直接贴出源码,供拜读:

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();
		}
		
		
		
	}
}

 

 

       怎么实现一个函数内使用多线程计算又保持函数体内代码的顺序执行,印象中使用多线程就是要摆脱代码块的顺序执行,但这里是把原本一个函数分拆成为两部分:一部分在C#线程中使用,另一部还是得在Unity的MainThread中使用,怎么解决呢,还得看例子:

 

//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();
        });
 
    });
}

        这个例子是对Mesh的顶点进行放缩,同时也是一个使用闭包(closure)和lambda表达式的一个很好例子。看完例子,是不是很有把项目中一些耗时的函数给拆分出来,D.S.Qiu就想用这个方法来改进下NGUI的底层机制(看下性能不能改进)。

 

 

 

小结:

       D.S.Qiu在编程技术掌握还是一个菜鸟,Thread还是停留在实现Runable接口或继承Thread的一个水平上,对多线程编程的认识还只是九牛一毛。本来我以为Loom的实现会比较复杂,当我发现只有100多行的代码是大为惊叹,这也得益于现在语言的改进,至少从语言使用的便利性上还是有很大的进步的。

       有了Loom这个工具类,在很多涉及UnityEngine对象的耗时计算还是可以得到一个解决方法的:

               如在场景中用A*算法进行大量的数据计算

               变形网格中操作大量的顶点
               持续的要运行上传数据到服务器
               二维码识别等图像处理

        Loom简单而又巧妙,佩服Loom的作者。

 

        如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

        转载请在文首注明出处:http://dsqiu.iteye.com/blog/2028503

更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)

 

参考:

①UnityGems:http://unitygems.com/threads/

Xiaoke's Bloghttp://blog.1vr.cn/?p=624

风宇冲:http://blog.sina.com.cn/s/blog_471132920101hh5d.html

4
0
分享到:
评论
5 楼 DSQiu 2015-07-16  
fat___lin 写道
sxmad 写道
这个,当切换场景时就不可以了。比如我后台一个Socket,用多线程异步处理的。返回消息肯定不是在主线程上,那QueueOnMainThread调用时没问题。但是如果切换场景这个就会报错,我的socket是单例。

Loom.cs 是要挂到场景里面的,只要不销毁loom挂的物体,应该是没有问题的。

  
4 楼 fat___lin 2015-07-15  
sxmad 写道
这个,当切换场景时就不可以了。比如我后台一个Socket,用多线程异步处理的。返回消息肯定不是在主线程上,那QueueOnMainThread调用时没问题。但是如果切换场景这个就会报错,我的socket是单例。

Loom.cs 是要挂到场景里面的,只要不销毁loom挂的物体,应该是没有问题的。
3 楼 sxmad 2014-12-16  
这个,当切换场景时就不可以了。比如我后台一个Socket,用多线程异步处理的。返回消息肯定不是在主线程上,那QueueOnMainThread调用时没问题。但是如果切换场景这个就会报错,我的socket是单例。
2 楼 DSQiu 2014-06-20  
chenyuan_noc 写道
QueueOnMainThread(Action action, float time)  这个函数中当time!=0时,会用到Time.time,这个貌似是不允许的吧,楼主有测过吗?

QueueOnMainThread 这是在Unity的主线程上运行的,所以是没有问题的;RunAsync 这个才是在其他线程上运行的。
1 楼 chenyuan_noc 2014-06-19  
QueueOnMainThread(Action action, float time)  这个函数中当time!=0时,会用到Time.time,这个貌似是不允许的吧,楼主有测过吗?

相关推荐

    Unity异步线程调用主线程脚本

    Unity异步线程调用主线程脚本程序,在Unity中异步线程调用主线程会报错,所以编写了一个Loom

    loom工具,unity多线程开发框架

    unity多线程开发框架,内含3个demo Flocking Massive:多线程Flocking Massive演示,prept运行在三星Galaxy S3 7000 Boids!!! 纹理模糊:表明它是多么容易使用这个框架,重复的任务,使用...

    [插件资源] Unity线程助手 Unity Thread Helper

    Unity Thread Helper是一款专为Unity引擎设计的插件,旨在帮助开发者更好地管理游戏或应用中的多线程任务。在Unity中,由于引擎的主要更新循环(如Update、LateUpdate等)是单线程执行的,直接在主线程外进行大量...

    Unity多线程工具类

    多线程是解决这个问题的有效方法之一,它可以让游戏在不影响主线程(负责渲染和用户交互)的情况下执行后台任务。本知识点将深入探讨Unity中的多线程工具类及其优化策略。 在Unity中,主线程对大部分引擎操作有严格...

    2021版 LOOM - Multi Threading Framework v1.7.zip Unity多线程开发包

    unity多线程开发框架,内含3个demo Flocking Massive:多线程Flocking Massive演示,prept运行 Boids!!! 纹理模糊:表明它是多么容易使用这个框架,重复的任务,使用“startmultithreadedworkloadexecution”...

    个人开发小工具--Unity主线程和子线程跳转调用

    Unity除了一些基本的数据类型,几乎所有的API都不能在子线程中调用,如果项目中有一段很耗时操作,unity可能会出现“卡死...因此针对这个问题再加上查找了一些资料,弄出了一个小工具,可以子线程与主线程的相互访问。

    unity 多线程

    综上所述,这些文件共同构成了一个在Unity环境中安全使用多线程的框架,包括线程创建、任务调度、线程间通信以及在主线程中安全执行代码的机制。通过这样的设计,开发者可以在保证Unity API的线程安全性的同时,充分...

    Unity3D中的多线程及使用多线程1

    在 Unity3D 中,主线程是唯一可以访问 Unity3D 组件、对象和 Unity3D 系统调用的线程,而其他线程不能访问这些对象和系统调用。 使用多线程可以提高应用程序的性能,因为可以充分利用多处理器的计算机资源。但是,...

    c#实现unity下可用的http多线程下载

    c#实现unity下可用的http多线程下载。主要是WWW接口的bundle函数不好用,而且性能也不高。支持多线程。支持单个文件和多个文件下载

    untiy 多线程demo

    在Unity中,直接使用.NET的线程(Thread)类进行多线程编程会受到一些限制,因为Unity的主循环并不支持在后台线程更新游戏状态。Loom.cs可能提供了一种安全的方式来管理和调度这些额外的线程,确保它们能与Unity引擎...

    unity下跨线程调用unity的内容

    主要实现了unity下开启线程,在线程中调用unity的内容,比如unity文本框,按钮和下拉框等unity自带的组件,必须通过相关的转换才能实现线程中调用unity的内容,否则,运行直接报错。只需要在unity项目开始调用的位置...

    C#子线程刷新主线程示例源码20121128

    C#子线程刷新主线程示例源码 功能介绍: 使用线程操作 1、实时显示当前时间 2、输入加数和被加数,自动出现... 使用了多线程实现了子线程刷新主线程 ,使用委托刷新主线程。 注意: 开发环境为Visual Studio 2012

    Unity协程(Coroutine)管理类——TaskManager工具分享

    在Unity引擎中,协程(Coroutine)是一种独特的编程机制,它允许开发者在游戏逻辑中实现类似于异步操作的功能,而不必使用回调函数或线程。协程可以暂停执行并在稍后的时间点继续,这对于处理耗时操作如加载资源、...

    主线程等待子多线程(无结果返回)执行完成再继续执行

    在多线程编程中,有时我们需要确保主线程在所有子线程执行完毕后才继续执行。这通常是为了保证数据的一致性或者按照特定顺序完成任务。"主线程等待子多线程(无结果返回)执行完成再继续执行"这个主题就涉及到如何在...

    Unity积雪和交互效果 积雪和交互效果

    Unity 积雪和交互效果 积雪和交互效果 积雪和交互效果 积雪和交互效果 积雪和交互效果 积雪和交互效果 积雪和交互效果积雪和交互效果积雪和交互效果积雪和交互效果积雪和交互效果积雪和交互效果积雪和交互效果积雪和...

    Unity 工具 之 日志保存本地,并且邮件附件发送到邮箱(线程中写入和邮件(Gmail)发送,不占用主线程).zip

    如果直接在主线程保存日志和发送日志,涉及到文件的读写,如果读写内容过大,会明显抢占主线程资源,造成卡顿,这里使用 Thread 进行日志的保存和邮件发送,避免造成主线程卡顿,这里简单说明,如果你有更好的方法,...

    用C#实现的unity线程初步

    Unity的多线程最佳实践通常推荐使用`UnityMainThreadDispatcher`这样的库来在主线程中异步执行任务,以避免并发问题。在上面的示例中,尝试在子线程中修改`count`变量会导致数据竞争,因为Unity的更新循环可能在同一...

    Unity3D多线程写法1

    在Unity3D中,多线程编程可以使用System.Threading命名空间中的Thread类来实现。Thread类提供了创建和管理线程的方法,可以让开发者轻松地创建和管理多个线程。 在上面的代码中,定义了一个名为MyThread的类,该类...

    Unity3D与Winform交互

    这种交互方式的一个常见应用场景是创建一个桌面工具,它使用Winform提供用户界面,而复杂的3D模拟或渲染则由Unity3D处理。 在实际开发中,可能还需要考虑性能优化、错误处理和兼容性问题。例如,确保Unity3D与...

Global site tag (gtag.js) - Google Analytics