入门篇参考之前的文章:
Unity 中的 async-await 咋用
首先,理解一下这两个关键字:
1.async
:可以把方法标识为异步
2.await
:异步方法在碰到await表达式之前都是使用同步的方式执行
await 后面等待什么呢?
C#提供了一个接口规范,就叫做 INotifyCompletion,命名空间是 System.Runtime.CompilerServices
其实现有:TaskAwaiter 以及泛型版本 TaskAwaiter
其实在Unity中使用 async,还是会在当前线程里面执行,怎么做到的呢?
void Start()
{
Debug.Log(Thread.CurrentThread.ManagedThreadId);//1
Test();
}
async void Test()
{
await Task.Delay(TimeSpan.FromSeconds(1f));
Debug.Log(Thread.CurrentThread.ManagedThreadId);//1
}
在Unity框架下,它的工作原理是因为:
Unity提供了一个名为 UnitySynchronizationContext 的默认 SynchronizationContext
它会自动收集每个帧排队的任何异步代码,并在主要的Unity线程上继续运行它们。
那么 SynchronizationContext 是啥?
SynchronizationContext 代表 线程上下文
主要有两个方法:
1.Send()
:是简单的在当前线程上去调用委托来实现(同步调用)。也就是在子线程上直接调用线程执行,等线程执行完成后子线程才继续执行。
2.Post()
:在线程池上去调用委托来实现(异步调用)。这是子线程会从线程池中找一个线程去调线程,子线程不等待UI线程的完成而直接执行自己下面的代码。
实现一个调度器实现原理如下:
主要是通过 SynchronizationContext.Current 进行上下文判断
1.如果是一个,就调用 Send 方法(或者直接调用 action)
2.如果不是一个,就调用 Post 方法
public class SyncContextUtil
{
//RuntimeInitializeOnLoadMethod 在物体的初始化之前就调用
// 流程:BeforeSceneLoad -> Awake -> OnEnable ->AfterSceneLoad -> Start
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Install()
{
UnitySynchronizationContext = SynchronizationContext.Current;
UnityThreadId = Thread.CurrentThread.ManagedThreadId;
}
//Unity主线程Id
public static int UnityThreadId
{
get; private set;
}
//SynchronizationContext 线程上下文
//主要有两个方法:
// 1.Send():是简单的在当前线程上去调用委托来实现(同步调用)。也就是在子线程上直接调用UI线程执行,等UI线程执行完成后子线程才继续执行。
// 2.Post():在线程池上去调用委托来实现(异步调用)。这是子线程会从线程池中找一个线程去调UI线程,子线程不等待UI线程的完成而直接执行自己下面的代码。
public static SynchronizationContext UnitySynchronizationContext
{
get; private set;
}
//实现Unity的多线程调度器
public static void RunOnUnityScheduler(Action action)
{
if (SynchronizationContext.Current == SyncContextUtil.UnitySynchronizationContext)
{
//本质上就是直接 action()
//SyncContextUtil.UnitySynchronizationContext.Send(_ => action(), null);
action();
}
else
{
SyncContextUtil.UnitySynchronizationContext.Post(_ => action(), null);
}
}
}