• 【Unity3D】协同程序


    1 简介

            1)协程概念 

            协同程序(Coroutine)简称协程,是伴随主线程一起运行的程序片段,是一个能够暂停执行的函数,用于解决程序并行问题。协程是 C# 中的概念,由于 Unity3D 的渲染操作是基于帧实现的,使用线程(Thread)不便于控制,因此 Unity3D 选择使用协程实现并发效果。

            协程并不是取代线程,而且抽象于线程之上。线程是系统调度的基本单位,是被分割的 CPU 资源;协程是组织好的代码流程,同一时间其实只有一个协程拥有运行权,相当于单线程的能力。协程需要线程来承载运行,线程是协程的资源,但协程不会直接使用线程,协程直接利用的是执行器(Interceptor),执行器可以关联任意线程或线程池,可以是当前线程、UI线程、新建线程.。

            协程是一个能够暂停执行的函数,在收到中断指令后暂停执行,并立即返回主函数,执行主函数剩余的部分,直到中断指令完成后,从中断指令的下一行继续执行协程剩余的部分。函数体全部执行完成,协程结束。协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。由于中断指令的出现,使得可以将一个函数分割到多个帧里执行

            2)中断指令

    1. // 协程在所有脚本的FixedUpdate执行之后,等待一个fixed时间间隔之后再继续执行
    2. yield return WaitForFixedUpdate();
    3. // 协程将在下一帧所有脚本的Update执行之后,再继续执行
    4. yield return null;
    5. // 协程在延迟指定时间,且当前帧所有脚本的 Update全都执行结束后才继续执行
    6. yield return new WaitForSeconds(seconds);
    7. // 与WaitForSeconds类似, 但不受时间缩放影响
    8. yield return WaitForSecondsRealtime(seconds);
    9. // 协程在WWW下载资源完成后,再继续执行
    10. yield return new WWW(url);
    11. // 协程在指定协程执行结束后,再继续执行
    12. yield return StartCoroutine();
    13. // 当返回条件为假时才执行后续步骤
    14. yield return WaitWhile();
    15. // 等待帧画面渲染结束
    16. yield return new WaitForEndOfFrame();

            补充:中断对象可以使用静态全局变量,避免产生过多临时对象、频繁触发 GC。 

            3)协程的执行周期

            4)协程与线程的区别

    • 一个线程可以有多个协程
    • 线程是协程的资源,协程通过 Interceptor 来间接使用线程这个资源;
    • 线程是同步机制,必须等待方法执行完才能返回执行后续方法;协程是异步机制,不需要等方法执行完就可以返回继续执行后续方法;
    • 线程是抢占式,进行线程切换,需要使用锁机制,多线程执行顺序具有一定随机性;协程是非抢占式的,多协程执行顺序由其启动顺序决定。

    2 协程的使用

            1)创建协程

    1. private IEnumerator CorutineTest() {
    2. Debug.Log("CorutineTest, 1");
    3. yield return null;
    4. Debug.Log("CorutineTest, 2");
    5. yield return new WaitForSeconds(0.05f);
    6. Debug.Log("CorutineTest, 3");
    7. yield return new WaitForFixedUpdate();
    8. Debug.Log("CorutineTest, 4");
    9. yield return new WWW("https://mazwai.com/download_new.php?hash=b524357ef93c1e6ad0245c04c721e479");
    10. Debug.Log("CorutineTest, 5");
    11. }

            2)启动协程

    1. private void Start() {
    2. // 形式一
    3. StartCoroutine(CorutineTest());
    4. StartCoroutine(CorutineTest("arg"));
    5. // 形式二, 此方式最多只能传1个参数
    6. StartCoroutine("CorutineTest");
    7. StartCoroutine("CorutineTest", "arg");
    8. // 形式三
    9. IEnumerator corutin = CorutineTest();
    10. StartCoroutine(corutin);
    11. }

            3)停止协程

    1. public void StopCoroutine(Coroutine routine);
    2. public void StopCoroutine(IEnumerator routine);
    3. public void StopCoroutine(string methodName); // 只能停止用字符串方法启动的协程
    4. public void StopAllCoroutines();
    5. yield break; // 跳出协程

            4)Start 协程

    1. private IEnumerator Start() { // 此时程序中不能再有其他Start方法
    2. yield return StartCoroutine(CorutineTest());
    3. }

    3 协程的应用

            1)案例一

    1. using System.Collections;
    2. using UnityEngine;
    3. public class CorutineController : MonoBehaviour {
    4. private void Start() {
    5. Debug.Log("Start-before");
    6. StartCoroutine(CorutineTest(3));
    7. Debug.Log("Start-after");
    8. }
    9. private void Update() {
    10. Debug.Log("Update");
    11. }
    12. private IEnumerator CorutineTest(int count) {
    13. Debug.Log("CorutineTest, start");
    14. yield return null;
    15. for (int i = 0; i < count; i++) {
    16. Debug.Log("CorutineTest, " + i);
    17. yield return null;
    18. }
    19. Debug.Log("CorutineTest, end");
    20. }
    21. }

            运行结果如下:

            从运行结果中可以看出:协程中的方法 CorutineTest 并没有阻塞 Satrt 的后续代码及 Update 方法执行。

             2)案例二

            在 Hierarchy 窗口创建 Image 对象,并给其添加 CorutineController 脚本组件,如下:

            CorutineController.cs

    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.IO;
    5. public class CorutineController : MonoBehaviour {
    6. private Image image;
    7. private void Awake() {
    8. image = GetComponent();
    9. StartCoroutine(DownLoadPicture());
    10. }
    11. private IEnumerator DownLoadPicture() {
    12. WWW www = new WWW("https://i0.hdslb.com/bfs/article/f04bc0a016e6d88c602047a39364a42cb27ea170.jpg@942w_531h_progressive.jpeg");
    13. // yield return www; // 等待www下载完毕
    14. while (!www.isDone) {
    15. Debug.Log("CorutineTest, progress = " + www.progress); // 打印下载进度
    16. yield return null; // 等待www下载完毕
    17. }
    18. Texture2D texture = www.texture;
    19. texture.name = "girl";
    20. image.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
    21. GetComponent().sizeDelta = new Vector2(texture.width, texture.height);
    22. File.WriteAllBytes(Application.dataPath + "/CorutineScene/girl.jpeg", www.bytes); // 保存图片
    23. }
    24. }

            运行效果:

  • 相关阅读:
    标题:Java中的逻辑与、短路与、逻辑或、短路或操作解析
    DDD之领域(Domain)和子域(Subdomain)
    数据库中的存储过程、游标、触发器与常用的内置函数
    方法研究在长安福特总装系统改进中的应用
    第六篇:元数据管理之“灵魂”三问
    excel中按多列进行匹配并对数量进行累加
    物联网感知-基于分布式光纤传感的石油石化管道综合监测
    C# winfrom应用程序添加图标
    SpringCloud学习笔记 - 链路监控 - SpringCloud Sleuth
    ‘javap‘ or ‘javac‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  • 原文地址:https://blog.csdn.net/m0_37602827/article/details/126679460