当前位置: 代码网 > it编程>游戏开发>unity > Unity多线程和协程

Unity多线程和协程

2024年08月03日 unity 我要评论
协程全称协同程序,不同于多线程,它不是一个线程,依附于Unity主线程。新开一个线程是独立的一个管道,和主线程并行执行;新开一个协程是在原线程之上开启,进行逻辑分时分步执行;协程的本质就是利用C#的迭代器函数“分步执行”的特点加上协程调度逻辑实现的一套分时执行函数的规则。

一.unity是否支持多线程?

unity支持多线程的使用,可以使用c#的thread类来创建和管理线程,只需要引入这个类:

但需要注意的是,在unity中,只有主线程(也称为渲染线程)可以访问unity对象,如gameobject、transform等,如果在其他线程中访问这些对象,会导致不可预期的结果。

因此,在使用多线程时,需要遵循一些规则:

  1. 不要在非主线程中访问unity对象;

  2. 不要在多个线程中同时修改同一个对象或变量,否则可能会导致竞态条件;

  3. 不要过度使用多线程,因为线程切换会带来额外的开销,而且多线程可能会增加代码复杂性和调试难度;

  4. unity中的多线程记得使用后要关闭,否则会在推出调试后一直运行,直到关闭unity或者改变相应的脚本代码。

示例: 

 新开的线程虽然不能访问unity中的对象,但是多线程可以进行一些复杂的逻辑计算:例如a*算法、网格计算,复杂的计算可能会卡住主线程,因此我们用副线程去计算。算好了,主线程再去调用。

示例:

 

 

 二.什么是协程?

协程全称协同程序,不同于多线程,它不是一个线程,依附于unity主线程。

  • 新开一个线程是独立的一个管道,和主线程并行执行;
  • 新开一个协程是在原线程之上开启,进行逻辑分时分步执行;

主要作用:

        协程适合处理需要分阶段执行的操作,如动画、延迟执行等。协程可以让程序在一定时间后再次执行,而不会阻塞主线程。这样可以避免线程切换带来的开销,提高程序性能。

        也就是说,协程是把可能会让主线程卡顿的耗时操作分时分步进行。

主要适用场景:

        异步加载文件、异步下载文件、场景异步加载、批量创建时防止卡顿。


        协程可以将一个函数分成多个部分,在每个部分执行完后暂停,等待下一次唤醒继续执行。如果不唤醒,这个协程就会被挂起。这种方式可以有效地控制程序的执行流程,使得我们可以更加灵活地控制程序的运行逻辑。

三.协程的使用

首先,要想使用协程,类要继承monobehavior;

在unity中,我们可以使用coroutine类来创建协程。通常情况下,我们会将协程定义为一个函数(使用ienumerator关键字),然后通过startcoroutine方法启动它。

示例:

        在这个例子中,我们定义了一个名为mycoroutine的协程函数,该函数使用yield语句分别暂停1秒和2秒,并在每次暂停后输出一条调试信息。然后,在start函数中,我们通过startcoroutine方法启动了这个协程。

        需要注意的是,协程函数必须返回ienumerator类型,而不是void类型。协程函数中使用yield语句来暂停执行,并返回一个对象,告诉协程系统应该何时继续执行。


        协程可以通过传递参数来控制它们的行为。我们可以将参数作为协程函数的参数,并在启动协程时传递给它。

 示例:

        在这个例子中,我们定义了一个名为mycoroutine的协程函数,该函数接受两个参数:一个字符串和一个浮点数。然后,在start函数中,我们通过startcoroutine方法启动了这个协程,并将"hello"和3分别作为参数传递给它。

        需要注意的是,在协程函数内部,我们可以像普通函数一样使用传递进来的参数。但是,在协程函数之外,我们不能直接访问协程内部的变量。如果需要在协程之间共享数据,可以考虑使用静态变量或其他线程安全的机制。

不同的yield return

  1. yield return null:暂停协程一帧,然后继续执行下一帧, 在update和lateupdate之间执行;

  2. yield return new waitforseconds(float seconds):暂停协程指定时间后继续执行,在update和lateupdate之间执行;

  3. yield return new waitforendofframe():暂停协程直到当前帧渲染完毕后(摄像机和gui)继续执行,在lateupdate之后的渲染相关处理之后执行;

  4. yield return new waitforfixedupdate():暂停协程直到下一次fixedupdate后继续执行,在fixupdate和碰撞检测相关函数之后执行;

  5. yield return startcoroutine(coroutine):暂停当前协程并启动一个新的协程,直到新的协程执行完成后继续执行当前协程。

  6. yield break:结束当前协程的执行。

 四.协程受对象和组件失活销毁的影响

当我们开启了一个协程,如果

  • 挂载此脚本的组件和物体销毁,协程将不会执行;
  • 物体失活协程不执行;
  • 组件失活协程执行。

五.协程的原理 

协程的本质:

        协程的本质是利用迭代器(iterator)来实现的。在c#中,迭代器是一种特殊的对象,它可以在循环中依次返回集合中的元素。在协程中,yield return语句返回的也是一个迭代器对象,当协程执行到yield return语句时,会将执行权交给调用者,并返回一个迭代器对象,等待调用者再次调用协程来继续执行。这样就实现了协程的暂停和继续执行功能。

        所以在脚本中调用协程相当于是把一个协程函数(迭代器)放入unity的协程调度器(startcoroutine()函数)中帮助我们管理进行执行,具体的yield return 后面的规则,也是unity定义的一些规则。

        我们通过获取ienumerator对象手动模拟协程的管理;

定义一个协程函数,无法单独调用:

 获取这个协程对象:

      ienumerator内部方法,属性如下:

执行movenext(),控制台会输出“test 1”,执行了一行语句;

执行reset(),控制台会输出 “1”, 打印的是yield return 的返回值;

如果打印current,控制台会输出yield return的参数值;

例如这种写法,reset会输出“123 vector3”,current会输出“123 (1,2,3)”;

 另一个问题来了,我们可以用movenext()和current来一步步执行协程函数。但是如果协程函数中有n个yield return 怎么办?写n个movenext()和current吗?

 注意:movenext()返回bool值,当协程函数内还有可以执行的内容时返回true,没有返回flase。

使用while输出执行协程函数中的所有值:

总结:

        协程的本质就是利用c#的迭代器函数“分步执行”的特点加上协程调度逻辑实现的一套分时执行函数的规则。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com