1.4 Unity协程

一、先说接口

接口是不能实例化的,想实例化接口,只能实例化继承了接口的类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace InterfaceTest {interface IMyInterface {void AMethod();}class Test : IMyInterface {public void AMethod() {Console.WriteLine("Extends from IMyInterface");}}class Program {static void Main(string[] args) {IMyInterface a = new Test();a.AMethod();Console.ReadKey();}}
}

如果实例化的接口引用 继承自同一个接口的不同的类,这个实例就可以调用这些类实现的该接口的所有方法。

二、C# yield

1.官方解释

如果你在语句中使用 yield 关键字,则意味着它在其中出现的方法、运算符或 get 访问器是迭代器。 通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerable 和 IEnumerator 模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅 IEnumerator<T>)。

2.实际上yield是一个语法糖

既然yield是在C#中的一个语法糖,那么就说明yield是对一种复杂行为的简化,就是将一段代码简化为一种简单的形式,方便我们程序员使用。

那么yield到底是对什么行为的简化。我们首先来看一下yield的使用场景。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConsoleApplication2
{class Program{static void Main(string[] args){foreach (int i in Power(2, 8, "")){Console.Write("{0} ", i);}Console.ReadKey();}public static IEnumerable<int> Power(int number, int exponent, string s){int result = 1;for (int i = 0; i < exponent; i++){result = result * number;yield return result;}yield return 3;yield return 4;yield return 5;}}
}

定义了一个返回类型是IEnumerable<int>的静态方法,方法名叫Power,如果按照平时做法,应该

return一个IEnumerablel<int>类型的参数。

public static IEnumerable<int> Power(int number, int exponent, string s){int result = 1;//接口不能实例化,我们这儿new一个实现了IEnumerable接口的List类//接口是不能实例化的!想实例化接口,只能实例化继承了接口的类IEnumerable<int> example = new List<int>();for (int i = 0; i < exponent; i++){result = result * number;(example as List<int>).Add(result);}return example;}

但是因为要new一个List,或者任何实现了IEnumerable接口的类型,这样也太麻烦了吧!要知道IEnumerable是一个常用的返回类型。每次使用都要new一个LIst,或者其他实现了该接口的类型。与其使用其他类型,不如我们自己定制一个实现了IEnumerable接口专门用来返回IEnumerable类型的类型。我们自己定制也很麻烦。所以微软帮我们定制好了。这个类是什么,那就是yield关键字这个语法糖。

3.示例1 自定义迭代 

public IEnumerable<int> Integers()
{yield return 1;yield return 2;yield return 4;yield return 8;yield return 16;yield return 16777216;
}

对Integers()的第一次调用返回1。第二次调用返回2,并且yield return 1;不再执行。

4.示例2 使用中的特殊情况

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{class Program{static void Main(string[] args){//这儿调用了方法。var test = Power(2, 8, "");Console.WriteLine("Begin to iterate the collection.");//Display powers of 2 up to the exponent of 8:foreach (int i in Power(2, 8, "")){Console.Write("{0} ", i);}Console.ReadKey();}public static IEnumerable<int> Power(int number, int exponent, string s){int result = 1;if (string.IsNullOrEmpty(s)){//throw new Exception("这是一个异常");Console.WriteLine("Begin to invoke GetItems() method");}for (int i = 0; i < exponent; i++){result = result * number;yield return result;}yield return 3;yield return 4;yield return 5;}}
}

按照我们的理解当我们 var test = Power(2, 8, "");的时候确实调用了Power方法。此时应该程序打印Console.WriteLine("Begin to invoke GetItems() method");然后继续执行 Console.WriteLine("Begin to iterate the collection.");方法。所以打印顺序应该是

Begin to invoke GetItems() method

Begin to iterate the collection.

但是我们运行的时候却发现

看了反编译代码,发现我们的打印方法并没有出现在Power方法中,而是被封装进了实现枚举接口的类方法  private bool MoveNext()中。所以方法不会立即被执行,而是在我们使用数据的时候被执行。如果对此机制不了解,就容易出现另外一些意想不到的问题。例如在Power方法中添加一些验证程序,如果不符合条件就抛出一个异常。这样的异常检查不会被执行。只有我们使用数据的时候才会执行。这样就失去了检查数据的意义。

另外使用yield还有一些注意事项:

你不能在具有以下特点的方法中包含 yield return 或 yield break 语句:

  • 匿名方法。 有关详细信息,请参阅匿名方法(C# 编程指南)。
  • 包含不安全的块的方法。 有关详细信息,请参阅unsafe(C# 参考)。

异常处理

不能将 yield return 语句置于 try-catch 块中。 可将 yield return 语句置于 try-finally 语句的 try 块中。

yield break 语句可以位于 try 块或 catch 块,但不能位于 finally 块。

如果 foreach 主体(在迭代器方法之外)引发异常,则将执行迭代器方法中的 finally 块。

三、协程!

1.线程和协程的概念

一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间平行运行,在线程里面可以开启协程,让程序在特定的时间内运行。

协程与协程之间是并行执行,与主线程也是并行执行,同一时间只能执行一个协程。

线程可以多线程,但是在Unity中只能在主线程中获取物体的组件、方法和游戏对象,但是协程中是可以正常获取游戏对象信息的!

在主线程中执行一个对资源消耗很大的操作时,就会影响游戏帧率,所以可以利用协程来做!

2.协程原理

协程是通过迭代器来实现的,通过关键字IEnumerator来定义一个方法

  • IEnumerator是一个实现迭代器功能的接口
  • IEnumerable是在IEnumerator基础上的一个封装接口,有一个GetEnumerator()方法返回IEnumerator

线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程由操作系统调度。

协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

3.使用协程

1.定义协程

通过IEnumerator定义一个协程,通过yield return来定义返回值

    IEnumerator Test(string str){Debug.Log("协程被启动了!"+ str);yield return null;}

2.启动协程

1.StartCoroutine(string methodName):通过协程的方法名(字符串形式)启动

2.StartCoroutine(string methodName, object values):带参数的通过方法名(字符串形式)启动

3.StartCoroutine(IEnumerator routine):通过调用方法的形式启动

    void Start(){//通过调用方法名来启动协程StartCoroutine("Test1");//通过调用方法名来启动协程StartCoroutine("Test2", "Hello World");//直接调用方法启动协程StartCoroutine(Test1());StartCoroutine(Test2("Hello World"));}IEnumerator Test1(){Debug.Log("协程被启动了!");yield return null;}IEnumerator Test2(string str){Debug.Log("协程被启动了!" + str);yield return null;}

3.停止协程

StopCoroutine和StopAllCoroutines两种方式都可以停止协程

1.StopCoroutine(string methodName):通过协程的方法名(字符串形式)关闭协程

2.StopCoroutine(IEnumerator routine):通过调用方法来关闭协程

3.StopCoroutine(Coroutine routine):通过指定的协程来关闭

4.StopAllCoroutines停止所有该脚本中启动的协程

    void StopTest(){//第一种方式:通过调用方法的形式来关闭协程StopCoroutine(Test1());//第二种方式:通过方法名(字符串)来关闭协程StopCoroutine("Test1");//第三种方式:通过指定的协程来关闭Coroutine a = StartCoroutine(Test1());StopCoroutine(a);//关闭该脚本中启动的所有协程!StopAllCoroutines();}

注意:使用哪一种开启协程的方法,就要用哪种方式来关闭!

4.yield Return

  • yield return null 暂停协程等待下一帧继续执行
  • yield return 0或其他数字 暂停协程等待下一帧继续执行
  • yield return new WairForSeconds(时间) 等待规定时间后继续执行
  • yield return new WaitForFixedupdate() 等到下一个固定帧数更更新
  • yield return new WaitForEndOfFrame() 等到所有相机画面被渲染完毕后更新
  • yield return StartCoroutine("协程方法名") 开启一个协和程(嵌套协程)
    void Start(){Debug.Log("a");}void Update(){Debug.Log("b");StartCoroutine(Test1());Debug.Log("d");}IEnumerator Test1(){Debug.Log("c");yield return null;Debug.Log("e");}

打印:第一帧a,b,c,d,第二帧b,c,d,e

代码遇到yield return之后会暂停一帧,跳出协程,等到下一帧再根据yield的生命周期位置来进行打印。

5.计时器案例演示

IEnumerator Test()
{Debug.Log("开始协程了");yield return new WaitForSeconds(3);//等待三秒执行下方代码块Debug.Log("三秒时间到了,执行此处代码!");
}

可以利用yield return new WairForSeconds(时间) 执行许多需要延迟的功能。

还有需要异步加载时都可以用到协程,比如AB包的异步加载、Resources资源的异步加载、场景的异步加载等。

4.底层原理

  • 协程是通过迭代器来实现功能的,通过关键字IEnumerator来定义一个迭代方法。
  • StartCoroutine 接受到的是一个 IEnumerator ,这是个接口,并且是枚举器或迭代器的意思。
  • yield 是 C#的一个关键字,也是一个语法糖,背后的原理会生成一个类,并且也是一个枚举器,而且不同于 return,yield 可以出现多次。
  • yield 实际上就是返回一次结果,因为我们要一次一次枚举一个值出来,所以多个 yield 其实是个状态模式,第一个 yield 是状态 1,第二个 yield 是状态 2,每次访问时会基于状态知道当前应该执行哪一个 yield,取得哪一个值。
  • 从程序的角度讲,协程的核心就是迭代器。
  • 想要定义一个协程方法有两个因素,第一:方法的返回值为 IEnumerator 。第二,方法中有 yield关键字。
  • 当代码满足以上两个条件时,此方法的执行就具有了迭代器的特质,其核心就是 MoveNext方法。
  • 方法内的内容将会被分成两部分:yield 之前的代码和 yield 之后的代码。yield之前的代码会在第一次执行MoveNext时执行, yield之后的代码会在第二次执行MoveNext方法时执行。
  • 而在Unity中,MoveNext的执行时机是以帧为单位的,无论你是设置了延迟时间,还是通过按钮调用MoveNext,亦或是根本没有设置执行条件,Unity都会在每一帧的生命周期中判断当前帧是否满足当前协程所定义的条件,一旦满足,当前帧就会抽出CPU时间执行你所定义的协程迭代器的MoveNext。
  • 注意,只要方法中有yield语句,那么方法的返回值就必须是 IEnumerator ,不然无法通过编译。

5.线程和协程的区别

  • 协程:即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程实际上是在一个线程中,只不过每个协程对CPU进行分时,协程可以访问和使用unity的所有方法和component。同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。
  • 线程:多线程是阻塞式的,每个IO都必须开启一个新的线程,但是对于多CPU的系统应该使用thread,尤其是有大量数据运算的时刻,但是IO密集型就不适合;而且thread中不能操作unity的很多方法和component。同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理。

线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/601293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的旅游管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的旅游管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

使用STM32微控制器驱动LCD1602显示器

驱动LCD1602显示器是嵌入式系统常见的任务之一&#xff0c;而STM32微控制器因其灵活性和丰富的外设而成为了广泛采用的解决方案。在这篇文章中&#xff0c;我们将探讨如何使用STM32微控制器来驱动LCD1602显示器。我们将从STM32的GPIO配置、延时函数以及LCD1602的初始化和写入数…

MIT_线性代数笔记:第 24 讲 马尔可夫矩阵;傅里叶级数

目录 马尔可夫矩阵 Markov matrices傅里叶级数 Fourier series 本讲学习马尔可夫矩阵和傅里叶级数&#xff0c;两者是关于特征值和投影矩阵的应用。 马尔可夫矩阵 Markov matrices A [ 0.1 0.01 0.3 0.2 0.99 0.3 0.7 0 0.4 ] A \begin{bmatrix} 0.1 & 0.01 & 0.3 \\…

所有单片机使用的汇编语言是统一的吗?

所有单片机使用的汇编语言是统一的吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&…

十大性能测试工具

这篇关于“性能测试工具”的文章将按以下顺序让您了解不同的软件测试工具&#xff1a; 什么是性能测试&#xff1f; 为什么我们需要性能测试&#xff1f; 性能测试的优势 性能测试的类型 十大性能测试工具 什么是性能测试&#xff1f; 性能测试是一种软件测试&#xff0c;可确…

Origin无法使用主题管理器相关功能或报错:Err, Save Theme dialog error!

问题描述 在使用origin绘图时&#xff0c;往往需要进行大批量绘制同样类型的图。如果每个图都不断地去修改相关设置&#xff0c;无疑是浪费了许多宝贵的时间。为了提高绘图效率&#xff0c;了解到了主题管理器&#xff0c;可在“工具–主题管理器”找到。 然而&#xff0c;当我…

自然语言处理24-T5模型的介绍与训练过程,利用简单构造数据训练微调该模型,体验整个过程

大家好,我是微学AI,今天给大家介绍一下自然语言处理24-T5模型的介绍与训练过程,利用简单构造数据训练微调该模型,体验整个过程。在大模型ChatGPT发布之前,NLP领域是BERT,T5模型为主导,T5(Text-to-Text Transfer Transformer)是一种由Google Brain团队在2019年提出的自然…

kotlin 单例

1.使用伴生对象&#xff08;companion object&#xff09;: class Singleton private constructor() { companion object { private val instance: Singleton by lazy { Singleton() } fun getInstance(): Singleton { return instance } } /…

帮你看懂广告行业名词解释,通俗易懂!!

一、DSP&#xff08;需求方平台&#xff09; 广告主只要在DSP平台投放广告&#xff0c;由DSP平台帮助广告主在多个媒体投放广告。&#xff08;即DSP帮助广告主对对接多个平台&#xff09; 二、ADX&#xff08;广告交易平台&#xff0c;Ad Exchange&#xff09; 连接媒体与广…

ARM工控机Node-red使用教程

嵌入式ARM工控机Node-red安装教程 从前车马很慢书信很远&#xff0c;而现在人们不停探索“科技改变生活”。 智能终端的出现改变了我们的生活方式&#xff0c;钡铼技术嵌入式工控机协助您灵活布建能源管理、大楼自动化、工业自动化、电动车充电站等各种多元性IoT应用&#xff…

Linux 编译安装 Nginx

目录 一、前言二、四种安装方式介绍三、本文安装方式&#xff1a;源码安装3.1、安装依赖库3.2、开始安装 Nginx3.3、Nginx 相关操作3.4、把 Nginx 注册成系统服务 四、结尾 一、前言 Nginx 是一款轻量级的 Web 服务器、[反向代理]服务器&#xff0c;由于它的内存占用少&#xf…

Android设备sdcard/tf卡不识别在电脑上可以

安卓tf卡无法使用现象 系统&#xff1a;安卓4.4 硬件&#xff1a;arm设备 t卡&#xff1a; 64GB 10 A1 microSD 出货后有用户反馈一批sdcard/tf卡用不了&#xff0e; 分析过程 拆了7台问题机&#xff0c;除一张t卡坏了外&#xff0c;其余卡插到电脑上后再放到设备上恢复正常…

bulk-RNA seq测序数据分析流程

假如有bulk-RNA测序的数据&#xff1a;TH1&#xff0c;TH2&#xff0c;TH3三个重复&#xff08;实验组&#xff09;&#xff0c;TW1&#xff0c;TW2&#xff0c;TW3三个重复&#xff08;对照组&#xff09; 准备工作 需要安装的软件&#xff08;如FastQC、Trimmomatic、HISAT…

adb shell getevent/sendevent

#### adb shell getevent 获取点击事件 100135925:/ # getevent add device 1: /dev/input/event2name: "mtk-tpd" /dev/input/event2: 0001 014a 00000001 /dev/input/event2: 0003 0039 00000088 /dev/input/event2: 0003 0035 00000072 /dev/input/event2: 00…

【Pytorch】学习记录分享10——TextCNN用于文本分类处理

【Pytorch】学习记录分享10——PyTorchTextCNN用于文本分类处理 1. TextCNN用于文本分类2. 代码实现 1. TextCNN用于文本分类 具体流程&#xff1a; 2. 代码实现 # coding: UTF-8 import torch import torch.nn as nn import torch.nn.functional as F import numpy as np…

14、接口

接口 ​ 接口interface&#xff0c;是一组行为规范的集合&#xff0c;就是定义一组未实现的函数声明。谁使用接口就是参照接口的方法定义实现它们。 type 接口名 interface {方法1 (参数列表1) 返回值列表1方法2 (参数列表2) 返回值列表2... }接口命名习惯在接口名后面加上er…

【机器学习:欧氏距离 】机器学习中欧氏距离的理解和应用

【机器学习&#xff1a;欧氏距离 】机器学习中欧氏距离的理解和应用 距离公式二维更高的维度点以外的物体属性欧几里得距离的平方概括历史 在数学中&#xff0c;欧氏距离’是指欧氏空间中任意两点之间的直线距离。这种距离可以通过应用勾股定理来计算&#xff0c;利用两点的笛卡…

如何停止一个运行中的Docker容器

要停止一个运行中的Docker容器&#xff0c;你可以使用以下命令&#xff1a; docker stop <容器ID或容器名> 将 <容器ID或容器名> 替换为你要停止的具体容器的标识符或名称。你可以使用以下命令查看正在运行的容器&#xff1a;docker ps 这将列出所有正在运行的…

Linux内核(2)-Makefile详解,必须要掌握的编译参数

1.版本号 VERSION 4 PATCHLEVEL 1 SUBLEVEL 152.MAKEFLAGS变量 MAKEFLAGS -rR --include-dir$(CURDIR) 包含当前目录及所有递归子目录 3.make V1编译输出 make V1 输出编译完整命令 ifeq ("$(origin V)", "command line")KBUILD_VERBOSE $(V) en…

再检查下这些测试思维面试题你都会了么?

创建坐席组的功能模块&#xff0c;如何进行测试用例设计&#xff1f; 解答&#xff1a; 功能测试&#xff0c;使用等价类划分法去分析创建坐席的每个输入项的有效及无效类&#xff0c;同步考虑边界值去设计对应的测试用例&#xff1a; 先进行冒烟测试&#xff0c;正常创建坐席…