聊聊 Unity3D 中的协同程序

U3D的协同程序使用起来十分方便,任何派生自MonoBehaivior的对象都可以通过执行StartCoroutine方法来开启一个协同。也可以通过StopCoroutine来关闭一个由该对象上面挂载的协同。这两个函数的具体的参数和使用此处不去细究,读者可以在网上找到海量介绍文章,本文旨在探究其实现背后的一些原理。

Unity3d 的协同通常会被拿来与线程进行比较,其实虽然协同表面上看上去和线程很像,但并不是真正意义的线程,而是一种伪线程

Unity3d是基于Mono的,所以要探究协同的最本质的实现代码则要看其源代码了,但是由于U3D并没有开源,所以很多C++代码是看不到的,我们只能通过反编译UnityEngine.dll看到接口的入口:

[MethodImpl(MethodImplOptions.InternalCall)]
private extern Coroutine StartCoroutineManaged(string methodName, object value);

可知该方法返回了一个Coroutine,Coroutine在UnityEngine.dll中定义如下:

// Coroutine
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Bindings;
using UnityEngine.Scripting;

/// <summary>
///   <para>MonoBehaviour.StartCoroutine returns a Coroutine. Instances of this class are only used to reference these coroutines, and do not hold any exposed properties or functions.</para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[NativeHeader("Runtime/Mono/Coroutine.h")]
[RequiredByNativeCode]
public sealed class Coroutine : YieldInstruction
{
	internal IntPtr m_Ptr;

	private Coroutine()
	{
	}

	~Coroutine()
	{
		ReleaseCoroutine(m_Ptr);
	}

	[MethodImpl(MethodImplOptions.InternalCall)]
	[FreeFunction("Coroutine::CleanupCoroutineGC", true)]
	private static extern void ReleaseCoroutine(IntPtr ptr);
}

包含了一个指针和一个Release的方法,因为是私有的且具体实现都是在C++中,我们不需要去关注了。

// YieldInstruction
using System.Runtime.InteropServices;
using UnityEngine.Scripting;

/// <summary>
///   <para>Base class for all yield instructions.</para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[UsedByNativeCode]
public class YieldInstruction
{
}

Coroutine继承自YieldInstruction,我们在写协同程序的时候经常会用到yield return,其后面便是可以跟一个YieldInstruction,比如WaitForSeconds、WaitForEndOfFrame等也都是YieldInstruction的子类,因此yield return后面也可以跟一个协同(Coroutine)。

例如:

private Coroutine coroutine;
private IEnumerator TestCoroutineFunc()
{
    yield return new WaitForSeconds(3);
    yield return coroutine;
}

可以理解为,等待另外一个协同的执行结束

协同的运行其实就是一个迭代的过程,yield这个语法糖就是为了简化遍历操作而设计的,否则需要写一堆代码才能实现遍历功能

StartCoroutine这个函数的参数即一个IEnumerator迭代器,我们让一个方法返回值为IEnumerator,在函数体中使用yield向该迭代器中添加需要遍历迭代的代码片段,该方法执行后将迭代器以参数的形式传递给StartCoroutine,剩下的就交给Mono的运行时来执行了。

TIPS:

如果游戏中需要在某帧执行大量任务造成主线程阻塞,可以考虑使用协程来分成若干帧执行来缓解。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值