【发布时间】:2012-02-19 19:25:40
【问题描述】:
背景:我正试图通过提出这个玩具问题来弄清楚如何实现延续/协程/生成器(无论以下称为什么)。环境是 gcc 4.6 和 linux 3.0 x86_64 上的 C++11。不可移植很好,但不允许使用外部库(boost.coroutine、COROUTINE 等)。我认为longjmp(3) 和/或makecontext(2) 和朋友可能会有所帮助,但不确定。
说明:
下面的玩具解析器应该解析等长的 as 和 bs 序列。即
((a+)(b+))+
使得第二个括号产生的长度等于第三个。
当它找到一个产生式(例如 aaabbb)时,它会输出它找到的 as 的数量(例如 3 个)。
代码:
#include <stdlib.h>
#include <iostream>
using namespace std;
const char* s;
void yield()
{
// TODO: no data, return from produce
abort();
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
if (*s != 'b')
abort();
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
// TODO: data available, continue into consume()
consume();
}
int main()
{
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
问题:
如您所见,consume 调用堆栈的状态必须在调用 yield 时保存,然后 produce 返回。当再次调用produce 时,必须通过从yield 返回来重新启动consume。挑战在于修改produce 调用consume 的方式,并实现yield 以便它们按预期运行。
(显然重新实现消费以便保存和重建其状态违背了练习的目的。)
我认为需要做的是类似于 makecontext 手册页底部的示例:http://www.kernel.org/doc/man-pages/online/pages/man3/makecontext.3.html,但不清楚如何将其转换为这个问题。 (我需要睡觉)
解决方案:
(感谢 Chris Dodd 的设计)
#include <stdlib.h>
#include <iostream>
#include <ucontext.h>
using namespace std;
const char* s;
ucontext_t main_context, consume_context;
void yield()
{
swapcontext(&consume_context, &main_context);
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
swapcontext(&main_context, &consume_context);
}
int main()
{
char consume_stack[4096];
getcontext(&consume_context);
consume_context.uc_stack.ss_sp = consume_stack;
consume_context.uc_stack.ss_size = sizeof(consume_stack);
makecontext(&consume_context, consume, 0);
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
【问题讨论】:
-
您的意思是
longjmp?我不知道任何拼写为longjump的函数。 -
makecontext 已弃用 iirc。
-
您为什么认为 makecontext 已被弃用?手册页上没有说明吗?
-
哦,这里是:“SUSv2,POSIX.1-2001。POSIX.1-2008 删除了 makecontext() 和 swap-context() 的规范,引用了可移植性问题,并建议应用程序是改写为使用 POSIX 线程。”虽然 POSIX 线程是抢占式的,创建了一个全新的克隆进程,但我认为用户态协作线程对于此类问题的性能会更高。
标签: c++ linux gcc c++11 coroutine