unity协程与线程同时使用

为什么要在协程中开启线程, 因为很多时候我们是需要线程执行完成后回到主线程的。然后主线程在继续执行后续的操作。
首先,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 =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  ,来重用类似的操作;
  1. using System;
  2. using System.Threading;
  3. ///
  4. /// A CustomYieldInstruction that executes a task on a new thread and keeps waiting until it's done.
  5. /// http://JacksonDunstan.com/articles/3746
  6. ///
  7. class WaitForThreadedTask : UnityEngine. CustomYieldInstruction
  8. {
  9. ///
  10. /// If the thread is still running
  11. ///
  12. private bool isRunning;
  13. ///
  14. /// Start the task by starting a thread with the given priority. It immediately executes the
  15. /// given task. When the given task finishes, returns true.
  16. ///
  17. /// Task to execute in the thread
  18. /// Priority of the thread to execute the task in
  19. public WaitForThreadedTask(
  20. Action task,
  21. ThreadPriority priority = ThreadPriority.Normal
  22. )
  23. {
  24. isRunning = true;
  25. new Thread(() => { task(); isRunning = false; }).Start(priority);
  26. }
  27. ///
  28. /// If the coroutine should keep waiting
  29. ///
  30. /// If the thread is still running
  31. public override bool keepWaiting { get { return isRunning; } }
  32. }


然后,我们就可以把加载过程变成这样了!!!!
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);}}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部