c++20加入了协程,为了性能实现的是无栈协程。有栈协程与无栈协程和有栈协程与无栈协程对协程的两种实现做了介绍,简单来说有栈协程是可以中断并恢复执行的subroutine,无栈协程是状态机。
使用 C 语言实现协程中介绍的这个无栈协程实现可以帮助更好的理解为什么说无栈协程是状态机。
coroutine.h只用三个宏(这里有简化)就实现了一个不可重入的无栈协程:
#pragma once
#define coBegin static int _SCRLINE=0; switch(_SCRLINE) { case 0:
#define coReturn(z) { _SCRLINE=__LINE__; return (z); case __LINE__:; }
#define coFinish(z) } return (z)
用一个例子来了解它的实现:
int fibonacci()
{
static int a=1, b=1;
coBegin;
coReturn(1);
coReturn(1);
for(;;)
{
b += a;
a = b - a;
coReturn(b);
}
coFinish(0);
}
这个函数用来计算斐波那契数列,把宏展开等价于:
int fibonacci()
{
static int a=1, b=1;
static int state=0;
switch(state)
{
case 0:
{state=1;
return 1;
case 1:;}
{state=2;
return 1;
case 2:;}
for(;;)
{
b += a;
a = b - a;
{state=3;
return b;
case 3:;}
}
}
return 0;
}
这里需要说明下switch case语句,case语句属于Labeled statements,就是goto后边的那个标签,任何一条语句都能在自己前面声明一个标识符作为标签名称,标签本身并不会改变控制流。所以case放在{}中或for循环中是没问题的。case 3后面跟了一条空语句是因为标签后面得有一条语句。
coReturn带有{}是为了支持跟在if语句后且没被{}包裹的情况。
c语言里的循环,可以认为是goto语句的语法糖,例如:
int test()
{
int sum = 0;
for(int i=0; i<10; ++i)
sum += i;
return sum;
}
与
int test()
{
int sum = 0;
{
int i=0;
loop:
if(i<10)
{
sum += i;
++i;
goto loop;
}
}
return sum;
}
是完全等价的。所以fibonacci就可以改写为:
int fibonacci()
{
static int a=1, b=1;
static int state=0;
switch(state)
{
case 0:
state=1;
return 1;
case 1:
state=2;
return 1;
case 2:
loop:
b += a;
a = b - a;
state=3;
return b;
case 3:
goto loop;
}
return 0;
}
这个状态机就很明显了。