异步
C# 异步
因为同步进程会出现阻塞的情况,为了避免卡顿,以及在处理数据时仍然可以接收用户新的访问,异步的思想就出现了
异步可以理解为,在调用异步函数的时候,函数会直接返回,然后剩下的部分等同于在一个新线程中运行
异步操作的关键字是asnyc
,await
,当一个方法具有 async
的签名时, 它会向编译器发出信号,说明此方法包含 await
语句;也包含异步操作。
windows官方定义:
异步操作后跟同步操作的这种组合是一个异步操作。 换言之,如果操作的任何部分是异步的,整个操作就是异步的。
Unity 异步
Unity还提供了一系列的异步操作,方便大家根据需要进行选择。这些异步操作包括Resources的加载、场景的加载、Asset Bundle的加载、NavMesh的生成等等。当然,这些异步操作的底层实现各不相同。有些是独立线程处理的,有些则是利用Unity内部的JobSystem,也有一些是二者结合使用的。
在Unity中使用协程搭配异步来写代码更佳,例如通过异步加载场景,在加载过程中通过每帧更新进度条给予玩家一个反馈,就可以在协程中调用异步加载场景的方法,然后暂停协程,等待当前一帧结束,然后更新进度条,反复之后,就完成进度条显示进度加载场景的效果。
这里有四点需要注意:
- 首先创建一个unity自定义
AsyncOperation
类的对象,来承接unity异步加载场景的返回值,这里的返回值就是刚才的那个AsyncOperation
类,作用是管理异步加载的进度,以及操控加载完毕后场景是否显示- 通过
yield return new WaitForEndOfFrame()
达到协程的一个暂停,不然很快进度条就跑过去了,可能这个时候场景数据还没有加载完- 异步加载场景函数调用一次就好,调用多次的话,出现bug先不说,如果所要加载的场景特别大的话,估计CPU就报废了(开个玩笑),所以在调用完异步之后,要做一个标记,循环再进来之后不进入异步的语句
- 因为加载场景,有可能在进度条还没有走完之前,场景就加载好了,所以要保证场景加载完毕之后场景不显示,需要用到
ao.allowSceneActivation = false;
语句,当然,在进度条加载完成之后,调用ao.allowSceneActivation = true;
语句显示所加载的场景。但这种想法终归是有点赌的成分在,在加载大场景的时候,很可能进度条到了100
,但是场景才加载了一部分,所以加载大场景的时候,可以选择使用异步给到的progress
来获取当前场景加载进度,这样比较好把握。但是问题又来了,Unity通常会加载到89%便不会更新progress
,但是此时场景已经加载完毕,所以再加一个循环使进度条到达100
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class LoadScene : MonoBehaviour
{
public string loadingSceneName;
private Button btn;
public Text loadingProcess;
private void Awake()
{
btn = GetComponent<Button>();
btn.onClick.AddListener(SceneShow);
}
void SceneShow()
{
StartCoroutine("Show"); // 协程手动开启,手动关闭
}
IEnumerator Show()
{
AsyncOperation async = SceneManager.LoadSceneAsync(loadingSceneName);
async.allowSceneActivation = false;
float displayProgress = 0;
float toProgress = 100f;
// 加载小场景可以使用这个循环即可
//while (displayProgress < toProgress)
//{
// displayProgress++;
// TextShow(displayProgress);
// yield return new WaitForEndOfFrame();
//}
// 加载大场景使用这两个循环
while (displayProgress < 90f)
{
displayProgress = async.progress * 100;
Debug.Log("第一个循环" + displayProgress);
TextShow(displayProgress);
yield return new WaitForEndOfFrame();
}
// Unity通常会加载到89%便会卡顿,此时场景已经加载完毕
while (displayProgress < toProgress)
{
displayProgress++;
Debug.Log("第二个循环" + displayProgress);
TextShow(displayProgress);
yield return new WaitForEndOfFrame();
}
//激活已经加载的场景
async.allowSceneActivation = true;
StopCoroutine("Show");
}
void TextShow(float displayProgress)
{
if (displayProgress >= 100f)
displayProgress = 100f; // 确保进度条不会显示大于100的数
loadingProcess.text = displayProgress.ToString("F2") + "%";
}
}