网站公告 | [优惠活动] 泰课在线11.11持续7天抢楼帖【优惠码放送】
查看: 591|回复: 14
收起左侧

[开发工具] Unity多线程(Thread)和主线程(MainThread)交互使用

[复制链接]

[开发工具] Unity多线程(Thread)和主线程(MainThread)交互使用[复制链接]

我了个去 发表于 2017-8-29 11:53:09 [显示全部楼层] |只看大图 回帖奖励 |倒序浏览 |阅读模式 回复:  14 浏览:  591
  
转: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定义的基本类型的函数可以在分线程运行,如      
[C#] 纯文本查看 复制代码
int i = 99;
print (i.ToString());
Vector3 x = new Vector3(0,0,9);
x.Normalize();

类的函数不能在分线程运行
  
      
[C#] 纯文本查看 复制代码
obj.name 

  
实际是get_name函数,分线程报错误:get_name  can only be called from the main thread.
  
      
[C#] 纯文本查看 复制代码
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这个类,叹为观止呀。直接贴出人家的介绍(没必要翻译了

Unity多线程(Thread)和主线程(MainThread)交互使用

Unity多线程(Thread)和主线程(MainThread)交互使用
):


Threads on a Loom
  
Our>  
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#] 纯文本查看 复制代码
using UnityEngine;  
using System.Collections;
  
using System.Collections.Generic;
  
using System;
  
using System.Threading;
  
using System.Linq;

  
public>  
{
  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中使用,怎么解决呢,还得看例子:
[C#] 纯文本查看 复制代码
//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 = vertices * 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的作者。


+1
598°C
14
  • vike2016
  • 2262006647
  • 无心牙
  • 我了个去
  • 无心牙
过: 他们
因分享而快乐,学习以自强!
vike2016 发表于 2017-9-1 19:24:15 显示全部楼层
可以可以
因分享而快乐,学习以自强!
2262006647 发表于 2017-9-2 11:54:52 显示全部楼层
#在这里快速回复#2262006647
因分享而快乐,学习以自强!
无心牙 发表于 2017-9-3 01:37:27 显示全部楼层
来untiy版看看
因分享而快乐,学习以自强!
我了个去
 楼主|
发表于 2017-9-3 15:20:03 显示全部楼层
多谢分享u3d内容
因分享而快乐,学习以自强!
无心牙 发表于 2017-9-4 05:02:38 显示全部楼层
抢不到沙发嘛
因分享而快乐,学习以自强!
逍遥客 发表于 2017-9-4 18:45:14 显示全部楼层
顶一个~~
因分享而快乐,学习以自强!
杯多纷 发表于 2017-9-5 08:27:49 显示全部楼层
好!!!!!!!!!!!!!
因分享而快乐,学习以自强!
ZackD 发表于 2017-9-6 01:20:24 显示全部楼层
我大unity666
因分享而快乐,学习以自强!
PleoMaxx 发表于 2017-9-6 18:13:00 显示全部楼层
会是几楼呢?
因分享而快乐,学习以自强!
jacob 发表于 2017-9-7 11:05:35 显示全部楼层
学习学习
因分享而快乐,学习以自强!
nicky880 发表于 2017-9-11 02:44:44 显示全部楼层
努力刷回复,赚点金币!
因分享而快乐,学习以自强!
karmic 发表于 2017-9-19 00:24:43 显示全部楼层
感谢楼主分享!!! 学习了
因分享而快乐,学习以自强!
qqhmitzk 发表于 2017-9-22 20:56:03 显示全部楼层
学习了学习了
因分享而快乐,学习以自强!
quangnmptit 发表于 2017-9-30 11:59:20 显示全部楼层
以上赚币操作的详情链接在这里
因分享而快乐,学习以自强!

本版积分规则

VR/AR版块|Unity3d|Unreal4|新手报道|小黑屋|站点地图|沪ICP备14023207号-9|【泰斗社区】-专注互联网游戏和应用的开发者平台 ( 沪ICP备14023207号-9 )|网站地图

© 2001-2013 Comsenz Inc.  Powered by Discuz! X3.2

1
QQ