协程定义

协程,从编程的角度看来,可以理解为用户自己能控制和调度的线程,可以理解为用户级别的线程。一个线程可以有多个协程,一个进程也可以有多个协程。

协程实现

协程的实现有多种方式

  • setjmp和longjmp
  • sigsetjmp和siglongjmp
  • ucontext函数族

本次采用ucontext实现,思路是:在进行函数跳转前,将获取当前调用者的上下文,并在当前调用者的上下文的基础上进行修改,制作新的函数堆栈,然后切换到函数的上下文。当函数执行结束,切换到调用者的上下文,让调用者继续执行协程调度。

void makecontext(ucontext_t *ucp, void (*func)(), int argc, …);

makecontext() 函数修改 ucp 指向的用户线程上下文,该上下文必须先前已通过调用 getcontext() 进行初始化,并为其分配了堆栈。 上下文被修改,以便通过使用提供的参数(类型为 int)调用 func() 来继续执行。 argc 参数必须等于提供给 makecontext() 的附加参数的数量,也等于提供给 func() 的参数数量,否则会出现不可知行为。
ucp->uc_link 参数必须在调用 makecontext() 之前初始化,如果ucp->uc_link等于 NULL,则函数返回后进程退出; 否则,函数要返回时隐式调用 setcontext(ucp->uc_link)。

int getcontext(ucontext_t *ucp);

getcontext() 函数将当前线程的执行上下文保存在 ucp 指向的结构中。 这个保存的上下文可以稍后通过调用 setcontext() 来恢复。

int setcontext(const ucontext_t *ucp);

setcontext() 函数使先前保存的线程上下文成为当前线程上下文,即当前上下文丢失并且 setcontext() 不返回。 相反,在 ucp 指定的上下文中继续执行,该上下文必须先前已通过调用 getcontext()、makecontext() 进行初始化。

int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);

swapcontext() 函数将当前线程上下文保存在 *oucp 中,并使 *ucp 成为当前活动的上下文。

实现如下

fiber.h 协程接口头文件

#ifndef TEST_FIBER_H
#define TEST_FIBER_H//定义一个任务
typedef void *FiberTask(void *);/*** 初始化协程环境* @param maxFiberCount 最大协程数量* @return*/
int InitFibersEnv(int maxFiberCount);int FreeFibersEnv();/*** 创建一个协程* @param task 传入一个任务* @param arg 任务的参数* @return*/
int CreateFiber(FiberTask *task, void *arg);/*** 协程调度* @return*/
int ScheduleFiber();#endif //TEST_FIBER_H

fiber.c 协程接口实现文件

#include "Fiber.h"#define _XOPEN_SOURCE#include <ucontext.h>
#include <stdlib.h>#define StackSize (1024 * 10)enum FiberStatusEnum {FIBER_IDLE,FIBER_RUNNING
};/*** 协程环境*/
struct FiberEnv {int fiberId;ucontext_t ucontext;FiberTask *fiberTask;void *taskArg;//任务参数char stackSpace[StackSize];short status;//状态};struct FibersEnv {int fiberCount;//协程数量struct FiberEnv **fiberEnvs;//协程环境ucontext_t ucontext;//主进程调度到上下文
};struct FibersEnv *g_FiberEnv;int InitFibersEnv(int maxFiberCount) {g_FiberEnv = calloc(1, sizeof(struct FibersEnv));g_FiberEnv->fiberCount = maxFiberCount;g_FiberEnv->fiberEnvs = (struct FiberEnv **) calloc(maxFiberCount, sizeof(struct FiberEnv *));for (int i = 0; i < maxFiberCount; ++i) {g_FiberEnv->fiberEnvs[i] = calloc(1, sizeof(struct FiberEnv));g_FiberEnv->fiberEnvs[i]->fiberId = i + 100;}return 0;
}int FreeFibersEnv() {if (!g_FiberEnv) {return 0;}struct FiberEnv **p = g_FiberEnv->fiberEnvs;while (*p != NULL) {free(*p);p++;}free(g_FiberEnv);g_FiberEnv = NULL;return 0;
}struct FiberEnv *FindFreeFiberSlot() {for (int i = 0; i < g_FiberEnv->fiberCount; ++i) {if (g_FiberEnv->fiberEnvs[i] && g_FiberEnv->fiberEnvs[i]->status == FIBER_IDLE) {return g_FiberEnv->fiberEnvs[i];}}return NULL;
}void RunTask(uint32_t highPtr, uint32_t lowPtr) {uint64_t ptr = (uint64_t) highPtr << 32 | (uint64_t) lowPtr;struct FiberEnv *fiberEnv = (struct FiberEnv *) ptr;//执行任务fiberEnv->fiberTask(fiberEnv->taskArg);
//    printf("fiber[%d] exec ok!\n", fiberEnv->fiberId);fiberEnv->status = FIBER_IDLE;//任务执行完成之后,切换到调用者到上下文去执行swapcontext(&fiberEnv->ucontext, &g_FiberEnv->ucontext);
}//创建一个协程
int CreateFiber(FiberTask *task, void *arg) {struct FiberEnv *fiberEnv = FindFreeFiberSlot();fiberEnv->status = FIBER_RUNNING;getcontext(&fiberEnv->ucontext);fiberEnv->ucontext.uc_stack.ss_size = StackSize;fiberEnv->ucontext.uc_stack.ss_sp = fiberEnv->stackSpace;fiberEnv->fiberTask = task;fiberEnv->taskArg = arg;uint32_t lowPtr = (uint64_t) fiberEnv;uint32_t highPtr = (uint64_t) fiberEnv >> 32;makecontext(&fiberEnv->ucontext, (void (*)()) RunTask, 2, highPtr, lowPtr);return 0;
}int ScheduleFiber() {for (int i = 0; i < g_FiberEnv->fiberCount; ++i) {if (!g_FiberEnv->fiberEnvs[i]) {continue;}swapcontext(&g_FiberEnv->ucontext, &g_FiberEnv->fiberEnvs[i]->ucontext);}return 0;
}

main.c 协程接口测试文件

#include "Fiber.h"
#include <stdio.h>void *PrintStrLine(void *str) {printf("%s\n", (char *) str);return NULL;
}int main(int argc, char **argv) {InitFibersEnv(10);CreateFiber(PrintStrLine, "hello-world1");CreateFiber(PrintStrLine, "hello-world2");CreateFiber(PrintStrLine, "hello-world3");ScheduleFiber();FreeFibersEnv();printf("exec ok...\n");return 0;
}

执行

gcc Fiber.c main.c -o testFiber

打印如下

hello-world1
hello-world2
hello-world3
exec ok...

注意

  • 可以注意到,makecontext为协程入口函数绑定上下文的时候,当需要为函数传递指针等变量的时候,因为makecontext是可变参,函数内部统一用4个字节去处理(有些系统可能已经做到了兼容)。而指针变量是8个字节(64位系统),故需要将指针变量拆分成高位和低位去处理。
  • 本文的实验环境位mac,mac默认的ucontext接口已经废弃,需要定义_XOPEN_SOURCE宏强制开启
  • 在用mac实验的时候,如果是封装在C++的类函数中,并使用了C++的集合,有些操作会出现奔溃问题,暂时理不清为什么。

C语言实现协程----初探相关推荐

  1. [通用技术]在不同语言中用协程实现全排列算法(C++/Lua/Python/C#)

    我这里实现全排列的基本算法如下(C++): 1 #include <algorithm> 2 #include <iostream> 3 #include <vector ...

  2. go 怎么等待所有的协程完成_GO语言基础进阶教程:Go语言的协程——Goroutine

    Go语言的协程--Goroutine 进程(Process),线程(Thread),协程(Coroutine,也叫轻量级线程) 进程进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为&qu ...

  3. Go语言中协程的概念和基本使用

    为什么80%的码农都做不了架构师?>>>    Go协程(Goroutine)是与其他函数同时运行的函数.可以认为Go协程是轻量级的线程.与创建线程相比,创建Go协程的成本很小.因此 ...

  4. 浅尝Go语言的协程实现

    文章目录 为什么需要协程 协程的本质 协程如何在线程中执行 GMP调度模型 协程并发 为什么需要协程 协程的本质是将一段数据的运行状态进行打包,可以在线程之间调度,所以协程就是在单线程的环境下实现的应 ...

  5. C语言关于协程的探究

    协程是一种用户级的轻量级线程,它可以在单线程里多个函数并发地执行协程,可以在主任务进行的同时,执行一些分支任务,以协助程序达到最终的效果,我们可以将协程成为用户态线程,但它与线程又有所区别. 协程和线 ...

  6. C 20 协程初探

    [导读]:C 20 终于引入了协程特性,给库作者提供了一个实现协程的机制,让用户方便使用协程来编写异步逻辑,降低了异步并发编程的难度.结合我最近协程的学习,在这里记录一下相关内容. 以下是正文 使用场 ...

  7. pdf 深入理解kotlin协程_协程初探

    Hello,各位朋友,小笨鸟我回来了! 近期学习了Kotlin协程相关的知识,感觉这块技术在项目中的可应用性很大,对项目的开发效率和维护成本有较大的提升.于是就考虑深入研究下相关概念和使用方式,并引入 ...

  8. Go语言入门到实战——14.Go语言的协程机制以及并发机制

    Go语言入门到实战--00主目录 在上一讲中我们学习了Go语言的包的知识已经依赖管理. 协程(groutine)是一种更加轻量级的线程(thread). 一.协程与线程得到比较 1.对于java而言, ...

  9. C语言中协程(coroutine)实现

    C语言协程库实现说明 代码实现 1. 当前支持的功能概览 1.1 创建任意数量协程并在协程中yield #include <stdio.h> #include <stdlib.h&g ...

最新文章

  1. wpf silverlight开发框架(prism)系列教程
  2. IO多路复用:select/poll/epoll
  3. “面试不败计划”:多线程
  4. 关于MM的几个经典问题及回答
  5. 排序算法——堆排序(C++)
  6. Javascript闭包概念剖析
  7. 处理目录的常用命令---Linux
  8. linux服务器指示灯,【转】明明白白你的Linux服务器——故障篇 | 旺旺知识库
  9. H.264中IDR帧和I帧区别
  10. 服务器能像客户端发信息吗,服务器怎么向客户端发信息吗
  11. 同心抗疫,IBM中国有限公司致客户的一封信
  12. hive中创建表失败
  13. 大数据云端实验室项目实战-微博舆情大数据分析有感
  14. topaz remask破解版|topaz remask抠图神器5破解版下载
  15. 图片尺寸放大不改变清晰度
  16. 2020年3月20日阿里内推笔试题
  17. android中的所有activity间动画跳转
  18. 软件测试度量分析,软件测试缺陷度量分析
  19. 视频转换成gif动图如何操作?教你三步完成视频转gif
  20. 张萌韩墨羽——打包升级兼容适配

热门文章

  1. 2020年T电梯修理答案解析及T电梯修理找答案
  2. 《花香满径》-幸福是朵三瓣花
  3. iOS中 视频直播功能-流媒体的使用
  4. Swan Song Alpha冲刺阶段博客目录
  5. base64转图片问题javax.imageio.IIOException: Invalid JPEG file structure: two SOI markers
  6. “盗梦”,指的是“在梦中盗取你的秘密”,而不是“盗取你的梦”
  7. 小学计算机绘画简报,广州市小学电脑绘画、小报制作比赛简报.doc
  8. 苹果App Store公布最新应用审核标准
  9. Java入门第53课——俄罗斯方块项目中的T类和J类
  10. PBOC-3DES-MAC加密算法(测试通过)