如何在c中的for循环内实现协程

问题描述 投票:0回答:4

这是部分代码:

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
从未改变过。

c coroutine
4个回答
4
投票

setjmp()
longjmp()
函数只能用于退出嵌套子例程,作为一种“抛出/捕获”。它们不能用于通过
return
或通过
longjmp()
重新进入已经退出的子例程。简而言之,它们不能这样使用。

在 C 中实现协程的唯一明确定义的方法是作为状态机。无法将协程实现为普通子例程,因为这与 C 堆栈不兼容。


3
投票

此处未定义您试图使用代码实现的目标。

引用

C11
,章节§7.13.2.1p2

longjmp
函数使用相应的 jmp_buf 参数恢复在同一程序调用中最近调用
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()
的单个步骤。然后循环交替调用它们。


0
投票

请查看“s_task”协程,这可能就是你的答案 --

https://github.com/xhawk18/s_task

特点--

  1. 用纯c和asm编写,无需c++
  2. 来自 boost 上下文的 asm 代码,稳健且有效
  3. 添加关键字 awaitasync
  4. 支持各种平台,如windows、linux(arm、x86、mips)、stm32、stm8
  5. 已集成libuv进行网络编程
  6. 用于协程之间通信的“事件”和“互斥”对象

0
投票

可以使用 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)) {}
}
© www.soinside.com 2019 - 2024. All rights reserved.