Unity在协程(Coroutines)内开启线程(Threading )

栏目: 后端 · 发布时间: 7年前

内容简介:Unity在协程(Coroutines)内开启线程(Threading )

孙广东  2017.6.13

http://blog.csdn.NET/u010019717

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

首先,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 =new MemoryStream();byte[] buffer =newbyte[1024];using(FileStream fileStream = File.OpenRead("/path/to/config.json")){while(true){int numBytesRead = fileStream.Read(buffer, 0, buffer.Length);if(numBytesRead ==0){break;}
				jsonStream.Write(buffer, 0, 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 )》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

实现领域驱动设计

实现领域驱动设计

Vaughn Vernon / 滕云 / 电子工业出版社 / 2014-3 / 99.00元

领域驱动设计(DDD)是教我们如何做好软件的,同时也是教我们如何更好地使用面向对象技术的。它为我们提供了设计软件的全新视角,同时也给开发者留下了一大难题:如何将领域驱动设计付诸实践?Vaughn Vernon 的这本《实现领域驱动设计》为我们给出了全面的解答。 《实现领域驱动设计》分别从战略和战术层面详尽地讨论了如何实现DDD,其中包含了大量的最佳实践、设计准则和对一些问题的折中性讨论。《实......一起来看看 《实现领域驱动设计》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换