首页 > 代码库 > spawn协程学习
spawn协程学习
对于IO密集型的程序,一般比较高效的做法是选择异步来实现,因为使用异步的方法更容易写出高效的程序。然而使用异步的话,经验较少的人往往会使自己的程序结构变得很混乱,进而导致程序的可读性变差。记得有人说过,在硬件飞速发展的现在,程序的可读性和可维护性的重要性在不断提高,甚至有一种更激进的说法,程序的可读性是第一位的。从Boost的1.54版本开始,coroutine引入了一种新型的协程,stackfull协程,之前Boost库的协程是stackless协程。对于这两者的区别,我仅知道stackless协程不可以使用局部变量,只能通过全局变量和类内部的变量实现信息传递,而stackfull协程则可以使用全局变量。可能是二者的实现方法不同吧,对于二者的效率差别,几乎可以忽略。哦,忘了介绍什么是协程了。。。
与子例程一样,协程也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程源自Simula和Modula-2语言,但也有其他语言支持。协程更适合于用来实现彼此熟悉的程序组件,如合作式多任务,迭代器,无限列表和管道。
由于协程不如子例程那样被普遍所知,最好对它们作个比较。子例程的起始处是惟一的入口点,一旦退出即完成了子例程的执行,子例程的一个实例只会返回一次。协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。子例程的生命期遵循后进先出(最后一个被调用的子例程最先返回);相反,协程的生命期完全由他们的使用的需要决定。
这里有一个很重要的特点,协程可以多次进入和退出,并且下一次进入的地方正好是上一次退出的地方。举个例子,
void fun(){ do1(); //1 do2(); //2 do3(); //3}
对于这样的一个函数过程,如果使用协程的方法实现的话,那么这个调用过程可以在do1的地方退出,然后在下一次进入,并且进入后直接从do2处开始执行。
那么一个协程适合做什么呢?你想,对于IO密集型程序中,IO系统调用往往不会很及时的返回,如果你选择使用同步的方法,你的程序往往可能会是下面的样子:
void fun(){ read(buf, ...); write(buf, ...); }
而如果你使用的是异步的话,你的程序可能会是下面的样子:
void fun(){ async_read(buf, read_handler);}void read_handler(){ do_somethiing//... async_write(buf, write_handler);}void write_handler(){ do_something//..}
这样的调用层次少的话,还可以接受,一旦多了,整个程序读起来会让人十分的崩溃。协程的出现解决了这个问题。
而如果你选择异步+协程的方法,你的程序不用等,并且会变得跟同步程序一样可读性很好。下面用我的一个简单的例子来说明stackfull协程的使用方法。程序主要实现了从控制台实现异步读和异步写的功能。代码十分简单,就不用说明了。(oh, 博客园的代码缩进简直就是shit, 算了直接贴代码地址吧)。
我一共用了两种方法实现该功能作为对比,一种是仅使用异步的方法,一种是适用异步+协程的方法。从整个代码的组织结构你可以看出使用了协程之后,整个程序的可读性大幅度提高,对于性能呢,你完全不用担心,几乎差别可以忽略。(注:这两份代码都是linux平台实现的,控制台异步输入输出的方法对于windows不适用。)
异步的代码地址:https://github.com/xiaopeifeng/CodeTricks/blob/master/async_say_love_fxp.cc
协程的代码地址:https://github.com/xiaopeifeng/CodeTricks/blob/master/spawn_say_love_fxp.cc
协程的具体实现方法,以后再做研究。
spawn协程学习