这是部分代码:
void a()
{
printf("entering a\n");
int i;
for(i = 0; i < 3; i++){
if(setjmp(a_buf) == 0) {
printf("A step %d\n", i);
b();
} else {
longjmp(b_buf, 1);
}
}
printf("returning from a\n");
}
void b()
{
printf("entering b\n");
int i;
for(i = 0; i < 5; i++){
if(setjmp(b_buf) == 0) {
printf("B step %d\n", i);
a();
} else {
longjmp(a_buf, 1);
}
}
printf("returning from b\n");
}
我有两个进程
a
和b
。如何使它们作为协程工作。
希望他们先做A Step 0
,然后B Step 0
,然后再回到A Step 1
......直到都完成。但看起来计数器i
从未改变过。
setjmp()
和longjmp()
函数只能用于退出嵌套子例程,作为一种“抛出/捕获”。它们不能用于通过 return
或通过 longjmp()
重新进入已经退出的子例程。简而言之,它们不能这样使用。
在 C 中实现协程的唯一明确定义的方法是作为状态机。无法将协程实现为普通子例程,因为这与 C 堆栈不兼容。
此处未定义您试图使用代码实现的目标。
引用
C11
,章节§7.13.2.1p2
函数使用相应的 jmp_buf 参数恢复在同一程序调用中最近调用longjmp
宏所保存的环境。如果没有这样的调用,或者调用来自另一个执行线程,或者包含setjmp
宏调用的函数在此期间已终止执行,或者如果setjmp
的调用宏位于具有可变修改类型的标识符的范围内,并且执行期间已离开该范围,行为未定义。setjmp
强调我的
关于什么算终止执行:
引用
C11
,章节§note248
例如,通过执行 return 语句或因为另一个
调用导致转移到嵌套调用集中较早的函数中的longjmp
调用。setjmp
所以,假设你先调用
a()
,它会在设置b()
后调用a_buf
。现在 b()
设置 b_buf
并跳回到 a
。此时 b
的执行已终止,如果跳回 b_buf
,则行为未定义。
解决您的问题的一个可能的解决方案可能是定义函数
a_step()
和 b_step()
,它们分别执行 a()
和 b()
的单个步骤。然后循环交替调用它们。
请查看“s_task”协程,这可能就是你的答案 --
https://github.com/xhawk18/s_task
特点--
可以使用 Duff 的设备,通过 STC 库中的协程来优雅地解决。
上帝螺栓:https://godbolt.org/z/354fhKKMx
#include <stc/coroutine.h>
#include <stdio.h>
struct a { int i; int cco_state; };
struct b { int i; int cco_state; };
int coro_a(struct a* g)
{
cco_routine (g) {
printf("entering a\n");
for (g->i = 0; g->i < 3; g->i++) {
printf("A step %d\n", g->i);
cco_yield();
}
cco_final:
printf("returning from a\n");
}
return 0; // done
}
int coro_b(struct b* g)
{
cco_routine (g) {
printf("entering b\n");
for (g->i = 0; g->i < 5; g->i++) {
printf("B step %d\n", g->i);
cco_yield();
}
cco_final:
printf("returning from b\n");
}
return 0;
}
int main(void)
{
struct a a = {0};
struct b b = {0};
while (coro_a(&a) | coro_b(&b)) {}
}