Lua教程
+ -

Lua 协同程序(coroutine)

2019-09-09 5 0

什么是协同(coroutine)?

Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

协同是非常强大的功能,但是用起来也很复杂。

线程和协同程序区别

线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。

在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。

基本语法

方法 描述
coroutine.create() 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
coroutine.resume() 重启coroutine,和create配合使用
coroutine.yield() 挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
coroutine.status() 查看coroutine的状态 注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序
coroutine.wrap() 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
coroutine.running() 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

以下实例演示了以上各个方法的用法:

-- coroutine_test.lua 文件
co = coroutine.create(
    function(i)
        print(i);
    end
)
coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- dead
print("----------")
co = coroutine.wrap(
    function(i)
        print(i);
    end
)
co(1)
print("----------")
co2 = coroutine.create(
    function()
        for i=1,10 do
            print(i)
            if i == 3 then
                print(coroutine.status(co2))  --running
                print(coroutine.running()) --thread:XXXXXX
            end
            coroutine.yield()
        end
    end
)
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2))   -- suspended
print(coroutine.running())
print("----------")

以上实例执行输出结果为:

1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868    false
suspended
thread: 0x7fb801c04c88    true
----------

coroutine.running就可以看出来,coroutine在底层实现就是一个线程。

当create一个coroutine的时候就是在新线程中注册了一个事件。

当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。

接下来我们分析一个更详细的实例:

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
co = coroutine.create(function (a , b)
    print("第一次协同程序执行输出", a, b) -- co-body 1 10
    local r = foo(a + 1)
    print("第二次协同程序执行输出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
    print("第三次协同程序执行输出", r, s)
    return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
end)
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

以上实例执行输出结果为:

第一次协同程序执行输出    1    10
foo 函数输出    2
main    true    4
--分割线----
第二次协同程序执行输出    r
main    true    11    -9
---分割线---
第三次协同程序执行输出    x    y
main    true    10    结束协同程序
---分割线---
main    false    cannot resume dead coroutine
---分割线---

以上实例接下如下:

  • 调用resume,将协同程序唤醒,resume操作成功返回true,否则返回false;
  • 协同程序运行;
  • 运行到yield语句;
  • yield挂起协同程序,第一次resume返回;(注意:此处yield返回,参数是resume的参数)
  • 第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)
  • yield返回;
  • 协同程序继续运行;
  • 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine

resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。

生产者-消费者问题

现在我就使用Lua的协同程序来完成生产者-消费者这一经典问题。

local newProductor
function productor()
     local i = 0
     while true do
          i = i + 1
          send(i)     -- 将生产的物品发送给消费者
     end
end
function consumer()
     while true do
          local i = receive()     -- 从生产者那里得到物品
          print(i)
     end
end
function receive()
     local status, value = coroutine.resume(newProductor)
     return value
end
function send(x)
     coroutine.yield(x)     -- x表示需要发送的值,值返回以后,就挂起该协同程序
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()

以上实例执行输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
……

0 篇笔记 写笔记

Java 多线程编程
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。这里定义和线程相关的另一个术语 -进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。......
CEF 多进程线程模型
CEF多进程由于CEF是基于chromium的封装开发,其本质不提供实质的额外关于浏览器的功能开发,只是对基于chromium接口的重新封装。所以cef和chromium的多进程一致。CEF3产生的独立进程使用进程间通信(IPC)进行通信.browser和render进程可以通过来回发送异步消息进行......
Ruby 多线程
每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程。Ruby 中我们可以通过 Thread 类来创建多线程,Ruby的线程是一个轻量级的,可以以高效的方式来实现并行的代码。创建 Ruby 线程要启......
Libuv 线程同步
Mutexeslibuv上的互斥量函数与pthread上存在一一映射。如果对pthread上的mutex不是很了解可以看这里。线程mutex函数UV_EXTERN int uv_mutex_init(uv_mutex_t* handle);UV_EXTERN void uv_mutex_destro......
Libuv 工作线程
在使用uv_queue_work的时候,你通常需要给工作线程传递复杂的数据。解决方案是自定义struct,然后使用uv_work_t.data指向它。一个稍微的不同是必须让uv_work_t作为这个自定义struct的成员之一(把这叫做接力棒)。这么做就可以使得,同时回收数据和uv_wortk_t。......
Lua 协同程序(coroutine)
什么是协同(coroutine)?Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。协同是非常强大的功能,但是用起来也很复杂。线程协同程序区别线程协同程序的主要区别在于,一个具有多个线程的程序可......
Python3 多线程
线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度程序的运行速度可能加快在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就......
Libuv 线程
线程会在内部使用,用来在执行系统调用时伪造异步的假象。libuv通过线程还可以使得程序异步地执行一个阻塞的任务。方法就是大量地生成新线程,然后收集线程执行返回的结果。当下有两个占主导地位的线程库:windows下的线程实现和POSIX的pthread。libuv的线程API与pthread的API在......
Libuv 线程通讯
很多时候,你希望正在运行的线程之间能够相互发送消息。例如你在运行一个持续时间长的任务(可能使用uv_queue_work),但是你需要在主线程中监视它的进度情况。下面有一个简单的例子,演示了一个下载管理程序向用户展示各个下载线程的进度。progress/main.cuv_loop_t *loop;u......
CEF Chromium多进程线程模型
Chromium浏览器是采用多进程模型开发的。Browser进程:浏览器的主进程,负责浏览器界面的显示,各个页面的管理,它是所有其它类型进程的祖先,负责它们的创建和销毁工作,有仅只有一个。Render进程:网页的渲染进程,负责页面的渲染工作,Blink/Webkit的渲染工作主要在这个进程中完成,可......
C++ 多线程
线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理: 基 于进程和基于线程。基于进程的多任务处理是程序的并发执行。基于线程的多任务处理是同一程序的片段的并发执行。多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一......
Ruby 块
您已经知道 Ruby 如何定义方法以及您如何调用方法。类似地,Ruby 有一个块的概念。块由大量的代码组成。您需要给块取个名称。块中的代码总是包含在大括号 {} 内。块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test ,那么您要使用函数 test 来调用这个块。您可以使用 yie......
Java 简介
Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称。由James Gosling和同事们共同研发,并在1995年正式推出。Java分为三个体系:JavaSE(J2SE)(Java2 Platform Standard Edition......
C# 多线程
1647线 程被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。线程是 轻 量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提......
设计模式 单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类......
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!