协程——uthread学习

协程——uthread学习

    • uthread说明
      • 细节
    • uthread代码

ucontext-人人都可以实现的简单协程库
github地址
vscode c++调试环境搭建
程序员应如何理解协程

在此记录一下协程的基本概念,后续再考虑实现手写的协程。


uthread说明

一个简单的C++用户级线程(协程)库

  • 一个调度器可以拥有多个协程
  • 通过uthread_create创建一个协程
  • 通过uthread_resume运行或者恢复运行一个协程
  • 通过uthread_yield挂起一个协程,并切换到主进程中
  • 通过schedule_finished 判断调度器中的协程是否全部运行完毕
  • 每个协程最多拥有128Kb的栈,增大栈空间需要修改源码的宏DEFAULT_STACK_SIZE ,并重新编译

更详细的介绍,请查看我的中文博客 人既无名的专栏.

细节

  • ctx保存协程的上下文,stack为协程的栈,栈大小默认为DEFAULT_STACK_SZIE=128Kb.你可以根据自己的需求更改栈的大小。

  • func为协程执行的用户函数,arg为func的参数

  • state表示协程的运行状态,包括FREE,RUNNABLE,RUNING,SUSPEND,分别表示空闲,就绪,正在执行和挂起四种状态。

  • 调度器包括主函数的上下文main,包含当前调度器拥有的所有协程的vector类型的threads,以及指向当前正在执行的协程的编号running_thread.如果当前没有正在执行的协程时,running_thread=-1.

下面给出一个简单例子,可以自己打断点调试一下,最重要的点:我感觉恢复上下文的时候,是恢复到下一行!我们只需要关注上下文之间的跳转即可!!!
在这里插入图片描述
下面也给出调试的配置launch.json

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) 启动",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/test1",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "将反汇编风格设置为 Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ]
        }

    ]
}

重点是这一行 "program": "${workspaceFolder}/test1"

uthread代码

总共就只有四个文件,这里都给出来,加了一点自己的注释理解

Makefile

all:
	g++  uthread.cpp  -g -c
	g++  main.cpp -g -o main uthread.o
clean:
	rm -f uthread.o main

uthread.h

/**
* @file  thread.h
* @author chenxueyou
* @version 0.1
* @brief   :A asymmetric coroutine library for C++
* History
*      1. Date: 2014-12-12 
*          Author: chenxueyou
*          Modification: this file was created 
*/

#ifndef MY_UTHREAD_H
#define MY_UTHREAD_H

#ifdef __APPLE__
#define _XOPEN_SOURCE
#endif 

#include <ucontext.h>
#include <vector>

#define DEFAULT_STACK_SZIE (1024*128)
#define MAX_UTHREAD_SIZE   1024

enum ThreadState{FREE,RUNNABLE,RUNNING,SUSPEND};

struct schedule_t;

typedef void (*Fun)(void *arg);

typedef struct uthread_t
{
    ucontext_t ctx;
    Fun func;
    void *arg;
    enum ThreadState state;
    char stack[DEFAULT_STACK_SZIE];
}uthread_t;

typedef struct schedule_t
{
    ucontext_t main;
    int running_thread;
    uthread_t *threads;
    int max_index; // 曾经使用到的最大的index + 1

    schedule_t():running_thread(-1), max_index(0) {
        threads = new uthread_t[MAX_UTHREAD_SIZE];
        for (int i = 0; i < MAX_UTHREAD_SIZE; i++) {
            threads[i].state = FREE;
        }
    }
    
    ~schedule_t() {
        delete [] threads;
    }
}schedule_t;

/*help the thread running in the schedule*/
static void uthread_body(schedule_t *ps);

/*Create a user's thread
*    @param[in]:
*        schedule_t &schedule 
*        Fun func: user's function
*        void *arg: the arg of user's function
*    @param[out]:
*    @return:
*        return the index of the created thread in schedule
*/
int  uthread_create(schedule_t &schedule,Fun func,void *arg);

/* Hang the currently running thread, switch to main thread */
void uthread_yield(schedule_t &schedule);

/* resume the thread which index equal id*/
void uthread_resume(schedule_t &schedule,int id);

/*test whether all the threads in schedule run over
* @param[in]:
*    const schedule_t & schedule 
* @param[out]:
* @return:
*    return 1 if all threads run over,otherwise return 0
*/
int  schedule_finished(const schedule_t &schedule);

#endif

uthread.cpp

/**
* @file  uthread.cpp
* @author chenxueyou
* @version 0.1
* @brief   :A asymmetric coroutine library for C++
* History
*      1. Date: 2014-12-12 
*          Author: chenxueyou
*          Modification: this file was created 
*/

#ifndef MY_UTHREAD_CPP
#define MY_UTHREAD_CPP


#include "uthread.h"
//#include <stdio.h>

void uthread_resume(schedule_t &schedule , int id)
{
    if(id < 0 || id >= schedule.max_index){
        return;
    }

    uthread_t *t = &(schedule.threads[id]);

    // 如果id对应的协程状态是挂起,就激活它的上下文,然后保存当前的上下文
    if (t->state == SUSPEND) {
        swapcontext(&(schedule.main),&(t->ctx));
    }
}

void uthread_yield(schedule_t &schedule)
{   
    // 只有当前有协程在运行,才能yield
    if(schedule.running_thread != -1 ){
        uthread_t *t = &(schedule.threads[schedule.running_thread]);
        t->state = SUSPEND;
        schedule.running_thread = -1;
    // 和resume正好相反
        swapcontext(&(t->ctx),&(schedule.main));
    }
}

// 用户函数
void uthread_body(schedule_t *ps)
{
    int id = ps->running_thread;

    // 说明有执行的协程,取出来,执行完了以后设置为空闲
    if(id != -1){
        uthread_t *t = &(ps->threads[id]);

        // 这里是执行用户函数,比如main.cpp中的func2和func3,执行完了回到这里
        t->func(t->arg);

        t->state = FREE;
        
        ps->running_thread = -1;
    }
}

int uthread_create(schedule_t &schedule,Fun func,void *arg)
{
    int id = 0;
    
    // 如果有空闲的协程,就退出,记录当前这个空闲的id
    for(id = 0; id < schedule.max_index; ++id ){
        if(schedule.threads[id].state == FREE){
            break;
        }
    }
    
    // 如果是最大id,就要更新最大索引了,设置为runable就绪
    if (id == schedule.max_index) {
        schedule.max_index++;
    }

    uthread_t *t = &(schedule.threads[id]);

    t->state = RUNNABLE;
    t->func = func;
    t->arg = arg;

    getcontext(&(t->ctx));
    
    // 设置协程上下文,下一个上下文为schedule里面保存的main上下文
    t->ctx.uc_stack.ss_sp = t->stack;
    t->ctx.uc_stack.ss_size = DEFAULT_STACK_SZIE;
    t->ctx.uc_stack.ss_flags = 0;
    t->ctx.uc_link = &(schedule.main);
    schedule.running_thread = id;
    
    // 将 uthread_body 函数的地址转换为一个无参数且无返回值的函数指针
    // uthread_body 是一个用户定义的函数,将在新线程中执行。
    makecontext(&(t->ctx),(void (*)(void))(uthread_body),1,&schedule);
    swapcontext(&(schedule.main), &(t->ctx));
    
    return id;
}

// 检查协程调度器中的所有协程是否都已完成执行
// 0代表还有协程没有执行完成,1代表都执行完成
int schedule_finished(const schedule_t &schedule)
{
    if (schedule.running_thread != -1){
        return 0;
    }else{
        for(int i = 0; i < schedule.max_index; ++i){
            if(schedule.threads[i].state != FREE){
                return 0;
            }
        }
    }

    return 1;
}

#endif

main.cpp

#include "uthread.h"
#include <stdio.h>

void func1(void * arg)
{
    puts("1");
    puts("11");
    puts("111");
    puts("1111");

}

void func2(void * arg)
{
    puts("22");
    puts("22");
    uthread_yield(*(schedule_t *)arg);
    puts("22");
    puts("22");
}

void func3(void *arg)
{
    puts("3333");
    puts("3333");
    uthread_yield(*(schedule_t *)arg);
    puts("3333");
    puts("3333");

}

void context_test()
{
    char stack[1024*128];
    ucontext_t uc1,ucmain;

    getcontext(&uc1);
    uc1.uc_stack.ss_sp = stack;
    uc1.uc_stack.ss_size = 1024*128;
    uc1.uc_stack.ss_flags = 0;
    uc1.uc_link = &ucmain;
        
    makecontext(&uc1,(void (*)(void))func1,0);

    swapcontext(&ucmain,&uc1);
    puts("main");
}

void schedule_test()
{
    schedule_t s;
    
    int id1 = uthread_create(s,func3,&s);
    int id2 = uthread_create(s,func2,&s);
    
    while(!schedule_finished(s)){
        uthread_resume(s,id2);
        uthread_resume(s,id1);
    }
    puts("main over");

}
int main()
{

    context_test();
    puts("----------------");
    schedule_test();

    return 0;
}

输出如下所示
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/582869.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决Could not locate zlibwapi.dll. Please make sure it is in your library path问题

nvidia官网已经下架了zlibwapi.dll的下载链接&#xff0c;可以按照下面方法。 1、在windows目录C:\Program Files\Microsoft Office\root\Office16\ODBC Drivers\Salesforce\lib下找到zlibwapi.dll文件 (如果下载不到可通过百度云) 链接&#xff1a;https://pan.baidu.com/s…

宝塔面板自定义设置告警通知webhook接口推送内容

前提 为了能够使用宝塔面板的自定义推送webhook对接到自己的推送系统&#xff0c;特意修改面板代码来支持自定义的推送系统。 环境 宝塔&#xff1a;Linux面板8.1.0 效果 步骤 主要修改文件路径如下 /www/server/panel/class/msg/web_hook_msg.py 源文件地址 完整文件链…

如何解决Edge浏览器显示“你的组织浏览器已托管”,导致无法正常打开网页问题?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

[ACTF2020 新生赛]BackupFile 1 [极客大挑战 2019]BuyFlag 1 [护网杯 2018]easy_tornado 1

目录 [ACTF2020 新生赛]BackupFile 1 1.打开页面&#xff0c;叫我们去找源文件 2.想到用disearch扫描&#xff0c;发现源文件index.php.bak 3.访问这个文件&#xff0c;下载一个文件&#xff0c;用记事本打开 4.翻译php代码 5.构造payload url/?key123&#xff0c;得到fl…

《与 Apollo 共创生态:我和 Apollo 7周年大会的心路历程》

目录 前言7周年大会开放协同写在最后 前言 Apollo开放平台的企业生态计划是一个激动人心的举措&#xff0c;它展现了Apollo团队长期以来的努力和成就。通过与全球开发者和合作伙伴的紧密合作&#xff0c;Apollo开放平台已经成为一个创新和技术交流的重要平台。企业生态计划的推…

LeetCode 104.二叉树的最大深度

题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;root [1,null,…

优化大型语言模型交互:提升查询和提示效果的26条原则

推荐下arxiv挂的一个提示词教程&#xff1a; https://github.com/VILA-Lab/ATLAS https://arxiv.org/abs/2312.16171 它提出了一套26条指导原则&#xff0c;改善和优化与大型语言模型&#xff08;LLMs&#xff09;的交互过程。通过这些原则&#xff0c;旨在简化对LLMs的查询和…

【软考高项】二十八、进度管理基础内容

一、管理基础 小型项目中&#xff0c;定义活动、排列活动顺序、估算活动持续时间及制定进度模型形成进度计划等过程的联系非常密切&#xff0c;可以视为一个过程&#xff0c;可以由一个人在较短时间内完成管理新实践 具有未完成项的迭代型进度计划:适应型生命周期的滚动式…

信息系统管理

目录 一、信息系统管理范围 1、规划和组织 2、设计和实施 ①、信息系统架构 Ⅰ、集中式架构 Ⅱ、分布式架构 Ⅲ、SOA&#xff08;面向服务的系统架构&#xff09; 3、运维和服务 ①、运行管理和控制 ②、IT服务管理 ③、运行与监控 Ⅰ、运行监控 Ⅱ、安全监控 4、…

Docker本地部署overleaf后,挖掘用户加密逻辑

overleaf的用户信息&#xff0c;保存在mongo数据库的users集合中。 用户密码则存在hashedPassword字段中 从开源的代码services\web\app\src\Features\Authentication\AuthenticationManager.js第303行可以找到密码加密逻辑。 本地可以通过下面的代码生成overleaf用户密码信息…

JAVA实现easyExcel批量导入

注解类型描述ExcelProperty导入指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写&#xff0c;默认第一个字段就是index0&#xff0c;以此类推。千万注意&#xff0c;要么全部不写&#xff0c;要么全部用index&#xff0c;要么全部用名字去匹配。…

投资蓄能之际,九安医疗如何进一步稳固主业“压舱石”?

体外诊断行业的消费环境变化&#xff0c;正从相关企业的发展中体现。 据梳理&#xff0c;随着疫情检测需求回落&#xff0c;2023年以来&#xff0c;菲鹏生物、雅睿生物、中翰生物等体外诊断公司&#xff0c;陆续主动撤回上市申请。 而已上市公司也正处于周期性调整阶段。4月2…

MobileNetV4 论文学习

论文地址&#xff1a;https://arxiv.org/abs/2404.10518 代码地址&#xff1a;https://github.com/tensorflow/models/blob/master/official/vision/modeling/backbones/mobilenet.py 解决了什么问题&#xff1f; 边端设备的高效神经网络不仅能带来实时交互的体验&#xff0c…

(学习日记)2024.05.10:UCOSIII第六十四节:常用的结构体(os.h文件)第三部分

之前的章节都是针对某个或某些知识点进行的专项讲解&#xff0c;重点在功能和代码解释。 回到最初开始学μC/OS-III系统时&#xff0c;当时就定下了一个目标&#xff0c;不仅要读懂&#xff0c;还要读透&#xff0c;改造成更适合中国宝宝体质的使用方式。在学完野火的教程后&a…

设计模式——保护性暂停

同步模式之保护性暂停 文章目录 同步模式之保护性暂停定义实现应用带超时版 GuardedObject扩展——原理之join扩展——多任务版 GuardedObject 定义 即 Guarded Suspension&#xff0c;用在一个线程等待另一个线程的执行结果 要点 有一个结果需要从一个线程传递到另一个线程&…

【逆向百例】百度翻译js逆向

关注它&#xff0c;不迷路。 本文章中所有内容仅供学习交流&#xff0c;不可用于任何商业用途和非法用途&#xff0c;否则后果自负&#xff01; 前言 目标 分析某度翻译接口&#xff0c;使用python获取翻译结果&#xff0c;并用pyinstaller打包成单文件可执行程序。 工具 ch…

python自定义交叉熵损失,再和pytorch api对比

背景 我们知道&#xff0c;交叉熵本质上是两个概率分布之间差异的度量&#xff0c;公式如下 其中概率分布P是基准&#xff0c;我们知道H(P,Q)>0&#xff0c;那么H(P,Q)越小&#xff0c;说明Q约接近P。 损失函数本质上也是为了度量模型和完美模型的差异&#xff0c;因此可以…

理解红黑树结构

红黑树的特性 节点是红色或黑色根是黑色叶子节点&#xff08;外部节点&#xff0c;空节点&#xff09;都是黑色&#xff0c;这里的叶子节点指的是最底层的空节点&#xff08;外部节点&#xff09;&#xff0c;下图中的那些null节点才是叶子节点&#xff0c;null节点的父节点在…

偏微分方程算法之五点菱形差分法

目录 一、研究目标 二、理论推导 三、算例实现 四、结论 一、研究目标 上个专栏我们介绍了双曲型偏微分方程的主要算法及实现。从今天开始&#xff0c;我们在新的专栏介绍另一种形式偏微分方程-椭圆型的解法。 研究目标选取经典的二维椭圆型方程&#xff08;也称泊松Poisso…

选对伪原创改写软件,文章写作不犯难!

文章写作在当下火热的自媒体的行业中是一项非常重要的技能&#xff0c;只要是参与做自媒体的朋友&#xff0c;想要在内容输出方面不出现困难的情况&#xff0c;那么文章写作的技能基本要具备&#xff0c;但是我们依然能看到有很多不擅长写作的朋友也做起了自媒体&#xff0c;并…
最新文章