一.unity是否支持多线程?
unity支持多线程的使用,可以使用c#的thread类来创建和管理线程,只需要引入这个类:
但需要注意的是,在unity中,只有主线程(也称为渲染线程)可以访问unity对象,如gameobject、transform等,如果在其他线程中访问这些对象,会导致不可预期的结果。
因此,在使用多线程时,需要遵循一些规则:
-
不要在非主线程中访问unity对象;
-
不要在多个线程中同时修改同一个对象或变量,否则可能会导致竞态条件;
-
不要过度使用多线程,因为线程切换会带来额外的开销,而且多线程可能会增加代码复杂性和调试难度;
-
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
-
yield return null:暂停协程一帧,然后继续执行下一帧, 在update和lateupdate之间执行;
-
yield return new waitforseconds(float seconds):暂停协程指定时间后继续执行,在update和lateupdate之间执行;
-
yield return new waitforendofframe():暂停协程直到当前帧渲染完毕后(摄像机和gui)继续执行,在lateupdate之后的渲染相关处理之后执行;
-
yield return new waitforfixedupdate():暂停协程直到下一次fixedupdate后继续执行,在fixupdate和碰撞检测相关函数之后执行;
-
yield return startcoroutine(coroutine):暂停当前协程并启动一个新的协程,直到新的协程执行完成后继续执行当前协程。
-
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#的迭代器函数“分步执行”的特点加上协程调度逻辑实现的一套分时执行函数的规则。
发表评论