Unity的异步任务处理

911 阅读4分钟

异步任务处理机制

unity的异步任务包括: Resources.LoadAsync, AssetBundle.LoadAssetAsync, AssetBundle.LoadAllAssetAsync), SceneManager.LoadSceneAsync。需要注意的是,这些方法都是指令性的,指令Unity引擎内部去启动异步任务,它们本身并没有处理异步任务的能力。当我们调用这些异步函数时,它们都会生成一个AsyncOperation对象,并且将它作为返回值返回。AsyncOperation对象是与异步任务对应的,用于实时的跟踪后台异步任务的处理状态,相当于暴露给用户的一个接口对象。这些AsyncOperation对象按照异步函数调用的先后顺序依次被添加进一个队列,按照先进先出的原则,每弹出一个AsyncOperation对象,就在后台进程处理它所代表的异步任务。这个任务队列是阻塞式的,也就是说只有当前的AsyncOperation所代表的异步任务被处理完毕,才会考虑继续弹出接下来的AsyncOperation对象(在后台处理它所代表的任务)。所以总结起来就是:异步函数启动的先后顺序,决定了队列中AsyncOperation对象的先后顺序,进而决定了它们所代表的异步任务在后台线程被Unity引擎处理的先后顺序。

需要注意的是,异步任务都是依次在一个后台线程(BackGround-Thread)中进行处理,而不是在主线程(main Thread)中进行处理,所以不会导致直接的帧阻塞。但是如果处理量过大,还是会影响cpu的性能,进而影响帧率。

同时,由于AsyncOperation类继承自YieldInstruction,所以它可以被协程利用,用作Yield return指令,判断异步任务是否处理完毕。

作为对上文细节的补充可以参考文档:docs.unity3d.com/ScriptRefer…

AsyncOperation类

Properties

allowSceneActivationAllow Scenes to be activated as soon as it is ready.
isDoneHas the operation finished? (Read Only)
priorityPriority lets you tweak in which order async operation calls will be performed.
progressWhat's the operation's progress. (Read Only)

Events

completedEvent that is invoked upon operation completion. An event handler that is registered in the same frame as the call that creates it will be invoked next frame, even if the operation is able to complete synchronously. If a handler is registered after the operation has completed and has already invoked the complete event, the handler will be called synchronously.

AsyncOperation.allowSceneActivation是一个全局的设定,即位于AsyncOperation队列后面的设定会覆盖前面的设定。参考(forum.unity.com/threads/iss…

例如SceneManagement.LoadSceneAsync().allowSceneActivation在设置为false的情况下,SceneManagement.LoadSceneAsync().progress达到90%就会停止(场景加载到90%),即异步任务暂停执行。这时,SceneManagement.LoadSceneAsync().isDone就一直为false,而这会导致AsyncOperation队列卡住,因为只有当前的AsyncOperation任务完成,才会进行下一个。如果此时,在后面的逻辑中,又启动了另外一个异步任务A,那么Unity将不会处理该异步任务A,因为前面的还没完成。不过,如果我们将该异步任务对应的AsyncOperation对象的allowSceneActivation设置为了true。那么由于该设定是全局的,前一个异步场景加载任务将继续执行,直到progress=100%,即场景加载成功,于是IsDone为true。队列的处理流不在被异步场景加载任务阻塞,于是unity开始着手处理异步任务A(在BackgroundThread中进行处理)。

注意上述,allowSceneActivatio的设定只对LoadSceneAsync生效,而对LoadScene()是无效的。

AsyncOperation对象应该在协程中被使用,而不是在程序中的任何其它地方。在协程中,AsyncOperation对象可以作为yield return的条件,用于判断异步任务是否成功,从而在进行下一步操作。

详情可以继续参考Unity的官方文档,尤其是AsyncOperation.priority和AsyncOperation.allowSceneActivation的介绍让我们知道了存在一个阻塞式的异步任务处理队列(由Unity引擎进行调度)。注:这里的阻塞指的是,只有当前的异步任务执行完毕,才会处理队列中的下一个异步任务,异步任务有一个专门的后台线程进行处理,所以并不会阻塞Main Thread。但是当任务量大的时候,会占用过多的CPU资源,进而影响帧率。