孙广东  2017.6.13

http://blog.csdn.NET/u010019717


为什么要在协程中开启线程, 因为很多时候我们是需要线程执行完成后回到主线程的。然后主线程在继续执行后续的操作。

首先,Unity官方也提到过《我的应用为什么应该使用线程而不是协程?


先说协程,协程方法可以一段接一段时间执行,但所有进程仍然由一个主线程完成。 如果一个协程尝试执行耗时的操作,整个应用程序暂时停止。

        Unity官方文章中也提到了 ,线程 和 线程的一些缺点!

主要是因为创建线程是一个开销比较大的操作。


更重要的是线程是危险的,所以必须非常小心同步的东西! 您还需要记住,Unity API本身就不是线程安全的,因此所有调用Unity API都应该从主线程完成。

当你计算一些高开销的或长期的操作,线程仍然可以是首选。 这方面的例子有:

  • 人工智能
  • 寻路
  • 网络通信
  • 文件操作


下面是协程的一个例子:

usingSystem.Collectons;usingUnityEngine;
 
class TestScript : MonoBehaviour
{class Config
	{publicstring Version;publicstring AssetsUrl;}
 
	void Start(){
		StartCoroutine(LoadConfig());}
 
	IEnumerator LoadConfig(){// 第一帧加载配置string json = File.ReadAllText("/path/to/config.json");
 
		// 等待直到第二帧yieldreturnnull;
 
		// 在第二帧解析配置
		Config config = JsonUtility.FromJson<Config>(json);
 
		// 等待直到第三帧yieldreturnnull;
 
		// 在第三帧使用配置
		Debug.Log("Version: "+ config.Version+"\nAssets URL: "+ config.AssetsUrl);}}
          上面协程将任务划分为三步骤,加载json, 解析成Config对象,使用配置。


           但是Load 加载可能是个耗时的操作, 我们在“Load”步骤中不在使用File.ReadAllText,我们可能会打开一个FileStream,并每一帧只加载文件的一部分。 它将很快变得相当复杂,但是我们可以加载更大的JSON文件,同时将工作扩展到几帧。
usingSystem.Collections;usingSystem.IO;usingSystem.Text;usingUnityEngine;
 
publicclass TestScript : MonoBehaviour
{class Config
	{publicstring Version;publicstring AssetsUrl;}
 
	void Start(){
		StartCoroutine(LoadConfig());}
 
	IEnumerator LoadConfig(){// 每帧加载1  Kb 大小,直到加载完成。
		MemoryStream jsonStream , buffer){break;}
				jsonStream, numBytesRead);yieldreturnnull;}}
 
		// 等到下一帧解析字符串yieldreturnnull;string json = Encoding.UTF8.GetString(jsonStream.ToArray());
 
		// 等到下一帧  转成 Config 对象yieldreturnnull;
		Config config = JsonUtility.FromJson<Config>(json);
 
		// 等到下一帧  使用配置yieldreturnnull;
		Debug.Log("Version: "+ config.Version+"\nAssets URL: "+ config.AssetsUrl);}}

看吧, 代码变得越来越复杂!
           一个替代方法是不分割任务,而是在另一个线程上运行它。 如果有一个空闲的CPU可以执行这个任务,这变得很好。 即使没有空闲的CPU,操作系统也会在线程和其他任何运行程序之间分配CPU时间。 这甚至可以由System.Threading.ThreadPriority枚举来控制,以创建低优先级和高优先级的线程。

使用线程的例子:
usingSystem.Collections;usingSystem.IO;usingSystem.Threading;usingUnityEngine;
 
publicclass TestScript : MonoBehaviour
{class Config
	{publicstring Version;publicstring AssetsUrl;}
 
	void Start(){
		StartCoroutine(LoadConfig());}
 
	IEnumerator LoadConfig(){// 第一帧开启线程
		Config config =null;bool done =false;new Thread(()=>{// 加载和解析Json, 跟Unity的帧无关。string json = File.ReadAllText("/path/to/config.json");
			config = JsonUtility.FromJson<Config>(json);
			done =true;}).Start();
 
		// 每帧都检查线程是否完成while(!done){yieldreturnnull;}
 
		// 线程完成 后的第一帧  使用配置
		Debug.Log("Version: "+ config.Version+"\nAssets URL: "+ config.AssetsUrl);}}

我们发现这个协程的作用是等待线程结束。
        那么我们可以考虑继承Unity提供的  CustomYieldInstruction  ,来重用类似的操作;
using System;
using System.Threading;

/// <summary>
/// A CustomYieldInstruction that executes a task on a new thread and keeps waiting until it's done.
/// http://JacksonDunstan.com/articles/3746
/// </summary>
class WaitForThreadedTask : UnityEngine.CustomYieldInstruction
{
	/// <summary>
	/// If the thread is still running
	/// </summary>
	private bool isRunning;

	/// <summary>
	/// Start the task by starting a thread with the given priority. It immediately executes the
	/// given task. When the given task finishes, <see cref="keepWaiting"/> returns true.
	/// </summary>
	/// <param name="task">Task to execute in the thread</param>
	/// <param name="priority">Priority of the thread to execute the task in</param>
	public WaitForThreadedTask(
		Action task,
		ThreadPriority priority = ThreadPriority.Normal
	)
	{
		isRunning = true;
		new Thread(() => { task(); isRunning = false; }).Start(priority);
	}

	/// <summary>
	/// If the coroutine should keep waiting
	/// </summary>
	/// <value>If the thread is still running</value>
	public override bool keepWaiting { get { return isRunning; } }
}

然后,我们就可以把加载过程变成这样了!!!!
usingSystem.Collections;usingSystem.IO;usingUnityEngine;
 
publicclass TestScript : MonoBehaviour
{class Config
	{publicstring Version;publicstring AssetsUrl;}
 
	void Start(){
		StartCoroutine(LoadConfig());}
 
	IEnumerator LoadConfig(){
		Config config =null;yieldreturnnew WaitForThreadedTask(()=>{string json = File.ReadAllText("/path/to/config.json");
			config = JsonUtility.FromJson<Config>(json);});
		Debug.Log("Version: "+ config.Version+"\nAssets URL: "+ config.AssetsUrl);}}

希望这篇文章对你有用!



Unity在协程(Coroutines)内开启线程(Threading )的更多相关文章

  1. 关于Unity中协程、多线程、线程锁、www网络类的使用

    协程 我们要下载一张图片,加载一个资源,这个时候一定不是一下子就加载好的,或者说我们不一定要等它下载好了才进行其他操作,如果那样的话我就就卡在了下载图片那个地方,傻住了.我们希望我们只要一启动加载的命 ...

  2. [Unity菜鸟] 协程Coroutine

    1.协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态. unity中StartCoroutine()就是协程,协程实际上是在一个线程中, ...

  3. Python 协程 - Coroutines

    协程 - Coroutines Awaitable Objects, Awaitable Objects 通常由 __await__() 生成, 而 Coroutine objects 是由 asyn ...

  4. UNITY所谓的异步加载几乎全部是协程,不是线程;MAP3加载时解压非常慢

    实践证明,以下东西都是协程,并非线程(thread): 1,WWW 2,AssetBundle.LoadFromFileAsync 3,LoadSceneAsync 其它未经测试 此问题的提出是由于一 ...

  5. Java的虚拟线程(协程)特性开启预览阶段,多线程开发的难度将大大降低

    高并发.多线程一直是Java编程中的难点,也是面试题中的要点.Java开发者也一直在尝试使用多线程来解决应用服务器的并发问题.但是多线程并不容易,为此一个新的技术出现了,这就是虚拟线程. 传统多线程的 ...

  6. Unity3D之协程(Coroutines & Yield )

    在Unity中StartCoroutine/yield return这个模式到底是怎么应用的? 比如你要一个方法进行一个比较耗时的复杂运算~同时又想让脚本流畅的进行其他操作而不是卡在那里等该方法执行完 ...

  7. 关于Unity的协程

    协程 认识协程 //协程不是多线程:是一段在主程序之外执行的代码 //协程不受生命周影响 //作用:能够口直代码在特定的时间执行. //1,延时操作 //2,等待某代码执行结束之后执行 /* 特点:1 ...

  8. C#神器 委托 + Unity神器 协程

    作为源生的C#程序员,可能已经非常了解委托(delegate).行动(Action)以及C#的事件了,不过作为一个半道转C#的程序员而言,这些东西可能还是有些陌生的,虽然委托并非是C#独创,亦非是首创 ...

  9. 关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.

    主要以结果为导向解释Spring 事务原理,连接池的消耗,以及事务内开启事务线程要注意的问题. Spring 事务原理这里不多说,网上一搜一大堆,也就是基于AOP配合ThreadLocal实现. 这里 ...

随机推荐

  1. boost shared_ptr weak_ptr

    文档: http://www.boost.org/doc/libs/1_57_0/libs/smart_ptr/shared_ptr.htm shared_ptr构造有个原型 template< ...

  2. beego——参数配置

    beego目前支持INI.XML.JSON.YAML格式的配置文件解析,但是默认采用了INI格式解析,用户可以通过简单的配置就可以获得很大的灵活性. 一.默认配置解析 beego 默认会解析当前应用下 ...

  3. Django:学习笔记(1)——开发环境配置

    Django:学习笔记(1)——开发环境配置 Django的安装与配置 安装Django 首先,我们可以执行python -m django --version命令,查看是否已安装django. 如果 ...

  4. k8s使用ceph作为后端存储挂载

    一.在ceph集群上操作: 1.创建池(主要使用存储类来进行持久卷的挂载,其他的挂载方式不好使也太麻烦):ceph osd pool create k8s 64 二.在k8s上操作: 1.安装客户端( ...

  5. Word 中设置图、表、公式、代码要与正文之间行间距

    一.概述 在撰写论文等文档时,常常对图.表.公式.代码要与正文之间行间距有要求.例如: (5)图.表.公式.代码要与正文之间有6磅的行间距. 二.设置方式 选中 图/表/公式/代码 与 图题/表头/- ...

  6. Poj 1755Triathlon 未Ac,先mark

    地址:http://poj.org/problem?id=1755 题目: Triathlon Time Limit: 1000MS   Memory Limit: 10000K Total Subm ...

  7. C语言中 ln(以自然对数e为底) lg(以十为底) 以及logab(以a为底,b为真数)的相关知识

    总所周知,我们在高中学过对数函数,记作y=logax.下面是百度百科关于对数函数的描述: 对数的定义:一般地,如果ax=N(a>0,且a≠1),那么数x叫做以a为底N的对数,记作x=logaN, ...

  8. vs2013 浏览器 browserlink 不停访问

  9. Bellman-Ford算法优化

    2017-07-27 16:02:48 writer:pprp 在BEllman-Ford算法中,其最外层的循环的迭代次数为n-1,如果不存在负权回路,需要迭代的次数是远远小于n-1; 如果在某一次迭 ...

  10. Java条件语句之 if

    生活中,我们经常需要先做判断,然后才决定是否要做某件事情.例如,如果考试成绩大于 90 分,则奖励一个 IPHONE 5S .对于这种“需要先判断条件,条件满足后才执行的情况”,就可以使用if 条件语 ...