# 第六节 stackless与await
await是一种特殊的语义抽象语法,它能将回调语法简写为简单的调用形式。在js、python、c#等语言有await关键字,c艹语言有co_await关键字都能实现同样的作用。
stackless作为对现有stackful的补充,从而通过其他途径来实现协程切换的功能。下面我给出一个示例,使用C++伪代码:
waitable<int> async_func1 ();
waitable<int> async_func2 ();
waitable<int> async_func () {
int n = co_await async_func1 ();
n += co_await async_func2 ();
co_return n;
}
我们一步一步来拆解:
我详细介绍介绍co_await的原理。首先,一个异步函数async_func
,函数内部有两个co_await,这将代码拆分为了三部分,也就是说函数实际被拆分为了三个函数,第一个函数执行完后,等待一个异步任务结束,再调用第二个函数,等待第二个任务执行完后,再调用第三个函数。用代码来描述就是:
void async_func_part0 () {
async_func1.run ();
}
void async_func_part1 () {
async_func2.run ();
}
int async_func_part2 (int part0_ret, int part1_ret) {
return part0_ret + part1_ret;
}
// 逻辑执行步骤:
async_func_part0 ();
int part0_ret = wait...;
async_func_part1 ();
int part1_ret = wait...;
async_func_part2 (part0_ret, part1_ret);
这儿我们通过一个函数,然后select方式实现,每次执行后,_index变量改变到下一位置,这样每次调用resume即可跳转到不同的位置,从而实现一个函数调用三次执行三块不同的功能代码。示例代码:
int _index;
bool run () {
select (_index) {
case 0:
// 省略...
return false;
case 1:
// 省略...
return false;
case 2:
// 省略...
return true;
}
}
然后,程序有很多上一段代码用到的变量,在等待之后,下一段代码还接着用。此处于是将其实现为,临时变量转类成员变量,这样就使得,异步函数结束前,每段功能代码都能访问到所有局部变量。
这儿给出最上面的异步函数,实际编译后,生成的代码的样子(不完全是实际样子):
struct async_func1_t;
struct async_func2_t;
void manage_async_func (async_t* _a1, async_t* _a2) {
// 省略实际实现...
// 函数功能:
// 1. 在任务队列中执行_a1.run ()
// 2. 如果为true则执行_a2.run ()
// 3. 如果为false则等待_a1需等待的另一个异步函数,等待完毕继续执行_a1.run (),重复这一步,直至返回true后执行_a2.run ()
}
struct async_func_t: public async_t {
int n;
int _index = 0;
int _ret;
async_func1_t *_async_func1;
async_func2_t *_async_func2;
bool run () {
switch (_index) {
case 0:
_async_func1 = new async_func1_t {};
manage_async_func (_async_func1, this);
_index = 1;
return false;
case 1:
n = _async_func1->_ret;
delete _async_func1;
_async_func2 = new async_func2_t {};
manage_async_func (_async_func1, this);
_index = 2;
return false;
case 2:
n += _async_func2->_ret;
_ret = n;
return true;
}
throw;
}
};