您好、欢迎来到现金彩票网!
当前位置:21点 > 子例程 >

怎么理解coroutine ?

发布时间:2019-07-18 08:35 来源:未知 编辑:admin

  有栈协程:系统调用就是一种有栈协程切换,系统调用因为执行系统态代码不能使用调用者的用户态内存空间栈(担心在栈中泄漏某些内核秘密)所以会切换去内核态的栈执行。如果是非阻塞系统调用,那么这次切换的过程会是:当前协程C-系统中断(或syscall)协程-当前协程C。如果是阻塞系统调用, 那么切换的过程会是:当前协程C-系统中断(或syscall)协程-发现暂时不能切换回来则切换去别的线程(的协程)-....cpu跑到别的线程上去游荡了.... -IO可用或者超时时钟中断导致从某个线程的协程切换到系统中断协程-io操作完毕后切换到协程C。任何独立持有执行栈的routine(例程)我们都把它叫有栈协程。线程之间天然持有不同栈,线程内部如果我们把栈再进行划分,让有的函数例程在某个栈上执行,有的函数例程在别的栈上执行,则他们叫不同的“有栈协程”。

  无栈协程:函数调用有一个隐式的参数,叫”返回地址“。我们可以让调用者再传递一个参数,叫”真返回地址“,这往往是个回调函数指针。调用者和被调用者之间可以约定, 被调用者操作未完成时,用”返回地址“返回(无阻塞系统调用下产生EAGAIN的情况);被调用者操作完成时,通过调用”真返回地址“这个函数指针来模拟返回(这个“重入”绝对不会有EAGAIN的产生了)。这就是完成回调的技法。可以用一些语法糖或者奇技淫巧把回调函数的内容(看起来)写在原有的调用函数的代码内,例如lambda,yuanzhubi/cort_proto等。 由于被调用函数需要持有调用函数所传递的回调地址,所以他得以一个对象的形式存在了;递归的,被调用对象也得持有调用对象的地址,这构成了一个对象链表或者叫回调链。因为这些对象拥有按照调用关系切换的能力,我们把这个对象叫做“无栈协程”。“无栈”表示通过“真返回地址”来进行回调执行的时候,没法访问“被调用者操作未完成时,用返回地址返回”这个过程中销毁的栈局部变量或者函数参数。这些变量需要存储在“未完成返回”过程中不会回收的地址上,所以如果用c++闭包来做这个协程对象的话,切记拷贝后使用(C++闭包定义即初始化在栈上的,所捕获的对象同理)。

  coroutine你可以将它看成一个用户态的线程(一般来它也提供了入口函数、调用的参数,以及你放置局部变量的栈),只不过它是你自己调度的,而且不同coroutine的切换不需要陷入内核态,效率比较高。

  linux有提供了getcontext swapcontext等接口来实现coroutine,windows貌似也有相关的。一般来说coroutine用在异步的场景比较好,异步执行一般需要维护一个状态机,状态的维护需要保存在全局里或者你传进来的参数来,因为每一个状态回调都会重新被调用。有了coroutine(stackfull)的话你可以不用担心这个问题,你可以像写同步的代码那样子,但其实底层还是异步的,只不过你在等待数据时执行的上下文会暂时被保存起来,等到数据来临再将上下文恢复继续执行。还有一种coroutine是stackless,它本质上也是状态机实现的,并不能在它上面让不同的状态共享局部变量,貌似outine就是这种。

  这里顺便广告一下:colaghost/coroutine_event · GitHub

  这是基于libevent简单封装的一个为异步IO提供同步访问的库,包装了accept connect read write这几个接口,同步的过程是利用coroutine的切换来实现的,有兴趣可以看下。

  排版还是不好,在博客上有详细版:理解Lua中最强大的特性-coroutine(协程)

  Lua所支持的协程全称被称作协同式多线程(collaborative multithreading)。Lua为每个coroutine提供一个独立的运行线路。然而和多线程不同的地方就是,coroutine只有在显式调用yield函数后才被挂起,同一时间内只有一个协程正在运行。

  Lua将它的协程函数都放进了coroutine这个表里,其中主要的函数如下

  摘取一段云风的代码来详尽解释协程的工作机制,在这段代码中,展示了main thread和协程co之间的交互:

  子例程的起始处是唯一的入口点,一旦return就完成了子程序的执行,子程序的一个实例只会运行一次。

  但是协程不一样,协程可以使用yield来切换到其他协程,然后再通过resume方法重入(reenter)到上次调用yield的地方,并且把resume的参数当成返回值传给了要重入(reenter)的协程。但是coroutine的状态都没有被改变,就像一个可以多次返回的subroutine。

  coroutine经常被用来和callback进行比较,因为通常来说,coroutine和callback可以实现相同的功能,即异步通信,比如说下面的这个例子:

  function runAsyncFunc( func, ... ) local current = coroutine.running func(function ( ) coroutine.resume(current) end, ...) coroutine.yield() end coroutine.create(function ( ) runAsyncFunc(bob.walkto, jane) runAsyncFunc(bob.say, hello) jane.say(hello) end) coroutine.resume(co)

  线程是操作系统级别的概念,现代操作系统都实现并且支持线程,线程的调度对应用开发者是透明的,开发者无法预期某线程在何时被调度执行。基于此,一般那种随机出现的BUG,多与线程调度相关。

  coroutine则是一个概念,windows上有所谓的fiber纤程实现,而好些语言中也自带coroutine的实现,比如Lua。与线程最大的不同是,coroutine的调度/挂起/执行开发者是可以控制的。另外coroutine也比线程轻量的多。要在语言层面实现coroutine,需要内部有一个类似栈的数据结构,当该coroutine被挂起时要保存该coroutine的数据现场以便恢复执行。

  关于协程,knuth解释的最简单明了,协程就是多入多出的子例程,另外从字面组成也非常切贴,就是可以协同运行的例程。

  协程本身与线程没有关系,只是如果从调度角度来看的话,相当于一种用户级协作式调度方案(好像windows fiber不完全是协作式的,但我倾向于是协作式才能称为协程)。至于goroutine,就是相当于把调度本身可以分配到多个线程中,或者叫异步调度。我在自己的实现中称之为concurrent coroutine(goroutine可能不能这么叫,已知的协程实现中都不包含真正的并行成分)。

  至于还有一些其他概念比如continuation,其实跟协程也没太大关系,只是continuation可以用于实现协程。这是当然的,谁让continuation是一切调用之母呢(is the mother of all function calls,类似有Continuation monad is the mother of all monads)。也有不基于continuation的实现,比如有人贴出的c实现,是基于duffs devices的跳转表(状态机)实现。也可以把两者结合起来用。

  我实现了一个还算通用的continuation for c,并在此基础上实现了完整的concurrent coroutine,可以参考:

  ,目前仅提交了continuation部分,包含一个closure实现,可以并行调用,暂未提交完整的concurrent部分以及event-driven部分。

  A Curious Course on Coroutines and Concurrency

  协程是在编译器层面用软件的方式实现的,线程是硬件中断实现的,所以协程的切换不会引起线程的切换,线程的切换有它自己的机制,跟协程无关,但是协程是站在线程的基础上实现运作的,没有线程就不可能实现协程。从实现的效果来看,两者是类似的,都是程序在运行一段时间后保存现场然后跳转执行另一段程序,区别在于程序员可以在协程指定何处进行跳转以及经历多长时间再跳转回来,相当于指定了协程的切换时间。而线程切换的时间一般是在操作系统内核时钟中断函数中指定的,程序员无法修改。

  我这三天一直在看coroutine相关的东西(因为C++20)。有了点想法,在这里说说吧。

  对于两个过程f和g的调用,一般我们都是f调用g,等待g执行完我们再返回。而coroutine则是f和g协作。执行顺序可以是f执行一点,把执行权(对于汇编可以理解为PC,对于编程语言可以理解为continuation)切换到g。g再执行一点,把执行权交给f。考虑一个由语法驱动词法的编译器,就是非常直白的coroutine。

  这样的程序是非常易懂的,即使不和callback这种糟糕的代码比,就跟很正常的递归比,在代码易读性方面是很有优势的。甚至会对cache更友好(因为方便做prefetching)。

  其实在汇编层面理解,coroutine无非就是jump而已,只不过要把当前的环境保存(保存到哪可以区分是有栈的还是无栈的coroutine)。

  而在编程语言方面来理解,coroutine就是保存当前的continuation,然后跳转到另一个continuation中。执行完再跳回来,在保存的continuation中继续执行。

  好像和如下代码是等价的呀(先不考虑保存continuation的问题)。

http://korinkorin.com/zilicheng/198.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有