热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

原文地址:http:dsqiu.iteye.comblog2028503熟悉Unity的developer都知道在Unity中的线程不能使用Unity的对象,但可以使用Uni

原文地址http://dsqiu.iteye.com/blog/2028503

         熟悉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)传入的函数。

 

直接贴出源码,供拜读:

C#代码  收藏代码
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using System.Collections.Generic;  
  4. using System;  
  5. using System.Threading;  
  6. using System.Linq;  
  7.   
  8. public class Loom : MonoBehaviour  
  9. {  
  10.     public static int maxThreads = 8;  
  11.     static int numThreads;  
  12.       
  13.     private static Loom _current;  
  14.     private int _count;  
  15.     public static Loom Current  
  16.     {  
  17.         get  
  18.         {  
  19.             Initialize();  
  20.             return _current;  
  21.         }  
  22.     }  
  23.       
  24.     void Awake()  
  25.     {  
  26.         _current = this;  
  27.         initialized = true;  
  28.     }  
  29.       
  30.     static bool initialized;  
  31.       
  32.     static void Initialize()  
  33.     {  
  34.         if (!initialized)  
  35.         {  
  36.           
  37.             if(!Application.isPlaying)  
  38.                 return;  
  39.             initialized = true;  
  40.             var g = new GameObject("Loom");  
  41.             _current = g.AddComponent();  
  42.         }  
  43.               
  44.     }  
  45.       
  46.     private List _actions = new List();  
  47.     public struct DelayedQueueItem  
  48.     {  
  49.         public float time;  
  50.         public Action action;  
  51.     }  
  52.     private List _delayed = new  List();  
  53.   
  54.     List _currentDelayed = new List();  
  55.       
  56.     public static void QueueOnMainThread(Action action)  
  57.     {  
  58.         QueueOnMainThread( action, 0f);  
  59.     }  
  60.     public static void QueueOnMainThread(Action action, float time)  
  61.     {  
  62.         if(time != 0)  
  63.         {  
  64.             lock(Current._delayed)  
  65.             {  
  66.                 Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action});  
  67.             }  
  68.         }  
  69.         else  
  70.         {  
  71.             lock (Current._actions)  
  72.             {  
  73.                 Current._actions.Add(action);  
  74.             }  
  75.         }  
  76.     }  
  77.       
  78.     public static Thread RunAsync(Action a)  
  79.     {  
  80.         Initialize();  
  81.         while(numThreads >= maxThreads)  
  82.         {  
  83.             Thread.Sleep(1);  
  84.         }  
  85.         Interlocked.Increment(ref numThreads);  
  86.         ThreadPool.QueueUserWorkItem(RunAction, a);  
  87.         return null;  
  88.     }  
  89.       
  90.     private static void RunAction(object action)  
  91.     {  
  92.         try  
  93.         {  
  94.             ((Action)action)();  
  95.         }  
  96.         catch  
  97.         {  
  98.         }  
  99.         finally  
  100.         {  
  101.             Interlocked.Decrement(ref numThreads);  
  102.         }  
  103.               
  104.     }  
  105.       
  106.       
  107.     void OnDisable()  
  108.     {  
  109.         if (_current == this)  
  110.         {  
  111.               
  112.             _current = null;  
  113.         }  
  114.     }  
  115.       
  116.       
  117.   
  118.     // Use this for initialization  
  119.     void Start()  
  120.     {  
  121.       
  122.     }  
  123.       
  124.     List _currentActions = new List();  
  125.       
  126.     // Update is called once per frame  
  127.     void Update()  
  128.     {  
  129.         lock (_actions)  
  130.         {  
  131.             _currentActions.Clear();  
  132.             _currentActions.AddRange(_actions);  
  133.             _actions.Clear();  
  134.         }  
  135.         foreach(var a in _currentActions)  
  136.         {  
  137.             a();  
  138.         }  
  139.         lock(_delayed)  
  140.         {  
  141.             _currentDelayed.Clear();  
  142.             _currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));  
  143.             foreach(var item in _currentDelayed)  
  144.                 _delayed.Remove(item);  
  145.         }  
  146.         foreach(var delayed in _currentDelayed)  
  147.         {  
  148.             delayed.action();  
  149.         }  
  150.           
  151.           
  152.           
  153.     }  
  154. }  

 

 

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

 

C#代码  收藏代码
  1. //Scale a mesh on a second thread  
  2. void ScaleMesh(Mesh mesh, float scale)  
  3. {  
  4.     //Get the vertices of a mesh  
  5.     var vertices = mesh.vertices;  
  6.     //Run the action on a new thread  
  7.     Loom.RunAsync(()=>{  
  8.         //Loop through the vertices  
  9.         for(var i = 0; i < vertices.Length; i++)  
  10.         {  
  11.             //Scale the vertex  
  12.             vertices[i] = vertices[i] * scale;  
  13.         }  
  14.         //Run some code on the main thread  
  15.         //to update the mesh  
  16.         Loom.QueueOnMainThread(()=>{  
  17.             //Set the vertices  
  18.             mesh.vertices = vertices;  
  19.             //Recalculate the bounds  
  20.             mesh.RecalculateBounds();  
  21.         });  
  22.    
  23.     });  
  24. }  

        这个例子是对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)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。



推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
author-avatar
vbppn65853
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有