之前在Unity项目中,使用了Loom作为返回主线程的一种工具。
Loom的原理其实很巧妙,就是利用Unity的Update方法来执行需要在主线程中调用的委托。
为什么需要在Update里操作呢,是因为Update是由Unity的主线程Call起的,因此在Update内执行的委托也一定是在主线程中运行,只不过运行前需要Lock一下_queue来防止其他线程访问。
但是因为在Update中执行,当运行Loom的RunOnMainThread方法时,其实依然有一定的延迟(虽然可以忽略不计),那么有没有更直接的办法呢?
之前在其他博客上看到了SynchronizationContext,也就是线程上下文,它可以允许往指定的线程中插入一个方法,使其立刻在指定线程中执行。
那么如果指定的线程为主线程,是不是就可以直接在主线程中插入一个方法来执行了呢?既然有了思路,那么就可以动手了。首先命名下这个工具,暂时叫做MainThreadManager,我们要确保获取的线程上下文一定是主线程的,一般是Unity内的核心管理器创建时,就可以对这个MainThreadManager进行初始化,使其获得主线程上下文。
public class MainThreadManager
{
// 单例模式
public static MainThreadManager Instance
{
get
{
return _instance;
}
}
private static MainThreadManager _instance;
static MainThreadManager()
{
_instance = new MainThreadManager();
}
// 指定线程的上下文,目前用于主线程
private SynchronizationContext m_mainThreadContext = null;
// 初始化这个管理器,使其获得当前线程的上下文
public void Init()
{
m_mainThreadContext = SynchronizationContext.Current;
}
// 在主线程中插入一个方法
public void RunOnMainThread(Action runOnMain)
{
m_mainThreadContext.Send(new SendOrPostCallback((needless)=>
{
runOnMain?.Invoke();
}), null);
}
}
至此,雏形也就完成了。那么我们可以来实践一下。
这是个测试的脚本,如果我们直接在子线程中修改Image的sprite,则必定抛出异常,
那么使用这个管理器来执行的话,也就是注释的部分来进行主线程切换,就正常地替换了Image的sprite了。
这只是一个思路,可以根据这个工具进行更多的扩展,来达到更多的效果。目前就是把学习到的技巧先总结一下,以后再更深入地了解学习。
===========Warning=============
最近发现循环访问这个方法会带来一定的性能问题,因此可能需要再深入研究一下。