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

时间 2014-03-09 11:04:04  ITeye-博客

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

直接贴出源码,供拜读:

  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<Loom>();
  42. }
  43.  
  44. }
  45.  
  46. private List<Action> _actions = new List<Action>();
  47. public struct DelayedQueueItem
  48. {
  49. public float time;
  50. public Action action;
  51. }
  52. private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
  53.  
  54. List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
  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. void OnDisable()
  107. {
  108. if (_current == this)
  109. {
  110.  
  111. _current = null;
  112. }
  113. }
  114.  
  115. // Use this for initialization
  116. void Start()
  117. {
  118.  
  119. }
  120.  
  121. List<Action> _currentActions = new List<Action>();
  122.  
  123. // Update is called once per frame
  124. void Update()
  125. {
  126. lock (_actions)
  127. {
  128. _currentActions.Clear();
  129. _currentActions.AddRange(_actions);
  130. _actions.Clear();
  131. }
  132. foreach(var a in _currentActions)
  133. {
  134. a();
  135. }
  136. lock(_delayed)
  137. {
  138. _currentDelayed.Clear();
  139. _currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));
  140. foreach(var item in _currentDelayed)
  141. _delayed.Remove(item);
  142. }
  143. foreach(var delayed in _currentDelayed)
  144. {
  145. delayed.action();
  146. }
  147.  
  148. }
  149. }

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

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

Loom工具使用分享的更多相关文章

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

    Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com ...

  2. 手机游戏渠道SDK接入工具项目分享(二)万事开头难

    一般接到任务后程序员们通常都开始着手进行技术调研了,但我这活是项目负责人.还有一大堆事情要先期准备,没人能帮忙. 一.人力配置 考虑的之前已经有一波人搞了大半年,但没有起色,先期也没有太大人力需求,所 ...

  3. 手机游戏渠道SDK接入工具项目分享(三)拨开云雾是个坑

    一直在纠结是先写框架设计还是先写掉过的坑,最后本这娱乐大众的态度先写掉过的坑让大家乐呵下. 项目开发过程中遇问题无数,回顾下8个大坑照成了项目一定程度上延期甚至返工. 项目一开始几个人把现有3家主流的 ...

  4. 手机游戏渠道SDK接入工具项目分享(一)缘起

    #剧情章节 # 上周刚结束一个外包的项目,开发手机游戏渠道SDK聚合接入工具的,现在有空回顾整理一下这个项目开发过程,因涉嫌商业秘密不会提供项目代码,只谈下开发思路和掉过的坑. 本人多年从事手机互联网 ...

  5. PHP Console工具使用分享

    PHP Console工具使用分享 http://www.open-open.com/lib/view/open1416193590414.html 您的评价:       不错  收藏该经验     ...

  6. UWP 手绘视频创作工具技术分享系列

    开篇先来说一下写这篇文章的初衷. 初到来画,通读了来画 UWP App 的代码,发现里面确实有很多比较高深的技术点,同时也是有很多问题的,扩展性,耦合,性能,功能等等.于是我们决定从头重构这个产品,做 ...

  7. UWP 手绘视频创作工具技术分享系列 - 全新的 UWP 来画视频

    从2017年11月开始,我们开始规划和开发全新的来画Pro,在12月23日的短视频峰会上推出了预览版供参会者体验,得到了很高的评价和关注度.吸取反馈建议后,终于在2018年1月11日正式推出了全新版本 ...

  8. .Net Excel 导出图表Demo(柱状图,多标签页) .net工具类 分享一个简单的随机分红包的实现方式

    .Net Excel 导出图表Demo(柱状图,多标签页) 1 使用插件名称Epplus,多个Sheet页数据应用,Demo为柱状图(Epplus支持多种图表) 2 Epplus 的安装和引用 新建一 ...

  9. C# 正则表达式测试工具与分享窗体自适应类

    放假刚回来,自己打算写一个正则表达式的测试工具,因为上次在网上用的一个在线正则表示测试工具就 没有很好的服务自己的,所以才有了现在的想法,想写一个C#开发者用的正则表达式测试工具!期间由于最大化时控件 ...

随机推荐

  1. spring_01

    1.框架 1.框架是解决什么问题的? 1.框架是用来解决代码冗余的问题 2.有利于团队的协作开发 3.框架是用来解决低耦合和高内聚的问题 4.解决健壮性和安全性 2.STRUTS2和hibernate ...

  2. 深入SpringBoot:自定义Endpoint

    前言 上一篇文章介绍了SpringBoot的PropertySourceLoader,自定义了Json格式的配置文件加载.这里再介绍下EndPoint,并通过自定EndPoint来介绍实现原理. En ...

  3. tapping of data 词义来源

    tapping of data 在数据交互的过程 数据被 窃听到. 例如, 网站的账户被泄露, 在用户登陆过程中, 其账号被第三方偷到. tapping 含义 看看 youdao 词典解释: n. [ ...

  4. c# 相对路径的一些资料

    1.获取和设置当前目录的完全限定路径. string str = System.Environment.CurrentDirectory; Result: C:\xxx\xxx 2.获取启动了应用程序 ...

  5. Salesforce select字段的多少对性能影响巨大

    Salesforce select字段的多少对性能影响巨大,第1个是select 144个字段,第2个是select 5个字段, 性能相差了7倍 "select Id,IsDeleted,M ...

  6. bzoj1029 [JSOI2007]建筑抢修

    贪心,按截止时间排序,然后按截止时间从小到大枚举维修的建筑,如果之前修理建筑的总时间+当前修理时间<=截止时间,那么答案+1,否则如果之前修理过的建筑中最大的修理时间>当前建筑修理时间,那 ...

  7. C# httprequest post 内容有百分号,部分特殊字符乱码问题

    哎没办法,还没完全脱离.net,已经一半了. http://stackoverflow.com/questions/7908581/how-to-encode-http-post-parameters ...

  8. POJ 2195:Going Home(最小费用最大流)

    http://poj.org/problem?id=2195 题意:有一个地图里面有N个人和N个家,每走一格的花费是1,问让这N个人分别到这N个家的最小花费是多少. 思路:通过这个题目学了最小费用最大 ...

  9. Windows:常见问题

    1.文件(夹)非法字符 Windows系统文件(夹)非法字符"\\/:*?\"<>|",不包含‘.’,但"."字符不能是文件(夹)的首字符 ...

  10. 使用percona-toolkit校验主从数据的一致性

    主从数据校验使用percona-toolkit工具集的以下两个工具(主库上使用): pt-table-checksum  #检查主从数据是否一致, pt-table-sync #把主库数据同步到从库 ...