孙广东  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. [转]Asp.net MVC 中Ajax的使用

    Asp.net MVC 抛弃了Asp.net WebForm那种高度封装的控件,让我们跟底层的HTML有了更多的亲近.可以更自由.更灵活的去控制HTML的结构.样式和行为.而这点对于Ajax的应有来说 ...

  2. T25健身视频全集+课表

    http://jianfei.39.net/thread-3639251-1.html T25健身视频全集+课表 强度适中 不伤膝盖! [复制链接]     zytttt         主题 好友 ...

  3. Unity,自带Random函数,上下限注意的地方

    Random.Range() 该函数有两个重载,分别是 float和 int 的,这两者还是有差别的,具体是: float型,随机值涵盖: 最小和最大值 Random.Range(0f,1f) 是有可 ...

  4. strtok()函数、fseek()函数、fwrite()函数、fread()函数的使用

    在电子词典这个项目过程中遇到了几个主要的问题,第一个是怎么解决把翻译分开这个.第二个事情就是怎么把结构体写到文件中.这两个问题,一个是关于字符串的操作一个是关于文件的操作. strtok函数 char ...

  5. 浏览器 Event对象 及 属性 的兼容处理

    摘自: http://blog.csdn.net/jiachunfeng/article/details/6448186 event对象 IE 中可以直接使用 event 对象,而 FF 中则不可以, ...

  6. 多种数据库之间 update的不同

    sql server update a set a.gqdltks=b.gqdltks,a.bztks=b.bztks from landleveldata a,gdqlpj b where a.GE ...

  7. C# 加法运算内部执行过程

    1,转换成32为或者64为的数字 2,进行运算 3,算出结果后为int32或者int64,进行类型转化得到自己需要的类型

  8. 【Head First Servlets and JSP】笔记21:从有脚本到无脚本

    可以建立多态的bean引用吗 使用type,但没有class scope属性默认为“page” 从有脚本到无脚本 1.快速搭建一个测试环境:输入用户名,返回“Hello, 用户名” index.htm ...

  9. MySQL-5.7 DELETE语句详解

    1.语法 (1)单表 DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [PARTITION (partition_name [, partit ...

  10. 差看windows上进程及线程

    转自:http://blog.csdn.net/swgsunhj/article/details/29552027 下载process exlporer: http://technet.microso ...