协程三篇之一(协程初接触)

赖勇浩(http://laiyonghao.com)

协程,又称微线程和纤程等,据说源于 Simula 和 Modula-2 语言(我没有深究,有错请指正),现代编程语言基本上都有支持,比如 Lua、ruby 和最新的 Google Go,当然也还有最近很让我惊艳的 falcon。协程是用户空间线程,操作系统其存在一无所知,所以需要用户自己去做调度,用来执行协作式多任务非常合适。其实用协程来做的东西,用线程或进程通常也是一样可以做的,但往往多了许多加锁和通信的操作。

 

下面是生产者消费者模型的基于抢占式多线程编程实现(伪代码):
// 队列容器
var q := new queue
// 消费者线程
loop
  lock(q)
  get item from q
  unlock(q)
  if item
    use this item
  else
    sleep
// 生产者线程
loop
  create some new items
  lock(q)
  add the items to q
  unlock(q)

 

由以上代码可以看到线程实现至少有两点硬伤:

 

1、对队列的操作需要有显式/隐式(使用线程安全的队列)的加锁操作。

 

2、消费者线程还要通过 sleep 把 CPU 资源适时地“谦让”给生产者线程使用,其中的适时是多久,基本上只能静态地使用经验值,效果往往不由人意。

 

而使用协程可以比较好的解决这个问题,下面来看一下基于协程的生产者消费者模型实现(伪代码):
// 队列容器
var q := new queue
// 生产者协程
loop
  while q is not full
    create some new items
    add the items to q
  yield to consume
// 消费者协程
loop
  while q is not empty
    remove some items from q
    use the items
  yield to produce

 

可以从以上代码看到之前的加锁和谦让 CPU 的硬伤不复存在,但也损失了利用多核 CPU 的能力。所以选择线程还是协程,就要看应用场合了。下面简单谈一下协程常见的用武之地,其中之一是状态机,能够产生更高可读性的代码;还有就是并行的角色模型,这在游戏开发中比较常见;以及产生器, 有助于对输入/输出和数据结构的通用遍历。

 

协程虽然如此之好,看是很长时间以来,因为受到基于堆栈的子例程实现的限制,并没有多少语言在其实语言或库中支持协程,所以线程作为一个替代者(当然,线程也有其超越协程之处)被广泛接受了。但是在今天,很多语言都内建了协程的支持,甚至是 C/C++ 语言。MS Windows 2000 以后的版本,都支持所谓的 Fiber,即纤程,其实就是协程的别称;在开源平台,POSIX 标准也定义了协程相关的标准,GNU Portable Threads 实现了跨平台的用户空间线程,即协程的另一种别称。在这百花齐放的时节,正是我们好好学习和利用它的时机。

 

接下来我将在第二篇中谈谈游戏中试用协程的三个场合。

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Unity协程的底层实现是基于C#的迭代器实现的。在C#中,使用yield关键字可以将方法转换为迭代器,通过迭代器可以实现协程的效果。Unity中的协程也是基于这个原理实现的。 如果要自己实现一个协程,可以按照以下步骤进行: 1. 定义一个委托,用于表示协程的执行体。 ```csharp public delegate IEnumerator CoroutineDelegate(); ``` 2. 定义一个协程类,保存协程的执行体和当前执行状态。 ```csharp public class Coroutine { private CoroutineDelegate m_CoroutineDelegate; private IEnumerator m_Enumerator; private bool m_IsDone; public bool IsDone { get { return m_IsDone; } } public Coroutine(CoroutineDelegate coroutineDelegate) { m_CoroutineDelegate = coroutineDelegate; m_Enumerator = m_CoroutineDelegate(); m_IsDone = false; } public void Update() { if (m_Enumerator != null && !m_IsDone) { if (!m_Enumerator.MoveNext()) { m_IsDone = true; } } } } ``` 3. 在需要使用协程的地方,创建一个协程对象并添加到一个协程管理器中。 ```csharp public class CoroutineManager : MonoBehaviour { private static CoroutineManager m_Instance; private List<Coroutine> m_Coroutines = new List<Coroutine>(); public static CoroutineManager Instance { get { if (m_Instance == null) { m_Instance = new GameObject("CoroutineManager").AddComponent<CoroutineManager>(); } return m_Instance; } } private void Update() { for (int i = m_Coroutines.Count - 1; i >= 0; i--) { Coroutine coroutine = m_Coroutines[i]; coroutine.Update(); if (coroutine.IsDone) { m_Coroutines.RemoveAt(i); } } } public Coroutine StartCoroutine(CoroutineDelegate coroutineDelegate) { Coroutine coroutine = new Coroutine(coroutineDelegate); m_Coroutines.Add(coroutine); return coroutine; } } ``` 4. 在协程中使用yield关键字来实现挂起和恢复。 ```csharp private IEnumerator MyCoroutine() { Debug.Log("Start Coroutine"); yield return null; Debug.Log("Wait One Frame"); yield return new WaitForSeconds(1.0f); Debug.Log("Wait One Second"); yield return new WaitForEndOfFrame(); Debug.Log("Wait End Of Frame"); } ``` 以上就是一个简单的协程实现。注意,实际应用中还需要考虑协程的取消、异常处理等问题,需要根据具体需求进行扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值