[libos源码学习 1] Liboc协程生产者消费者举例
文章目录
- 1. CoRoutineEnv_t结构体用于管理协程环境
- 3 Liboc协程生产者消费者例子
- 4 Liboc协程生产者消费者, 为什么队列不需要上锁?
- 5. 两个协程访问资源不需要加队列吗
- 5. 参考
1. CoRoutineEnv_t结构体用于管理协程环境
struct stCoRoutineEnv_t
{
stCoRoutine_t *pCallStack[ 128 ];
int iCallStackSize;
stCoEpoll_t *pEpoll;//for copy stack log lastco and nextco
stCoRoutine_t* pending_co;
stCoRoutine_t* occupy_co;};中文描述
stCoRoutineEnv_t结构体是用于协程环境的结构体,它包含了以下几个成员:1. pCallStack:这是一个数组,用于存储协程的调用栈。数组的大小是128,这意味着这个环境可以同时运行128个协程。2. iCallStackSize:这是一个整数,用于记录当前调用栈的大小,也就是正在运行的协程的数量。3. pEpoll:这是一个指向stCoEpoll_t结构体的指针,用于管理协程的事件循环。4. pending_co:这是一个指向stCoRoutine_t结构体的指针,用于表示待处理的协程,也就是在事件循环中等待执行的协程。5. occupy_co:这是一个指向stCoRoutine_t结构体的指针,用于表示当前被占用的协程,也就是正在执行的协程。
中文描述
stCoRoutineEnv_t结构体用于管理协程环境,包括协程的调用栈、调用栈的大小、事件循环以及正在执行和等待执行的协程。它的主要作用是提供一个运行协程的环境,协程可以在这个环境中进行切换,实现并发执行。
结构体成员详解
- pCallStack:这是一个数组,用于存储协程的调用栈。数组的大小是128,这意味着这个环境可以同时运行128个协程。
- iCallStackSize:这是一个整数,用于记录当前调用栈的大小,也就是正在运行的协程的数量。
- pEpoll:这是一个指向stCoEpoll_t结构体的指针,用于管理协程的事件循环。事件循环是协程的核心,它负责管理协程的切换和调度。
- pending_co:这是一个指向stCoRoutine_t结构体的指针,用于表示待处理的协程,也就是在事件循环中等待执行的协程。
- occupy_co:这是一个指向stCoRoutine_t结构体的指针,用于表示当前被占用的协程,也就是正在执行的协程。
结构体的使用
stCoRoutineEnv_t结构体通常用于协程库或框架中,用于管理协程的运行环境。开发者可以使用这个结构体来创建、管理和调度协程,以实现并发执行。
struct stCoRoutine_t
{stCoRoutineEnv_t *env;pfn_co_routine_t pfn;void *arg;coctx_t ctx;char cStart;char cEnd;char cIsMain;char cEnableSysHook;char cIsShareStack;void *pvEnv;//char sRunStack[ 1024 * 128 ];stStackMem_t* stack_mem;//save satck buffer while confilct on same stack_buffer;char* stack_sp; unsigned int save_size;char* save_buffer;stCoSpec_t aSpec[1024];};
#2. stCoRoutine_t协程的实体
stCoRoutine_t结构体定义了一个协程的实体,包含了协程的环境、协程体、参数、上下文、标志位、堆栈内存以及特殊变量等信息。下面是对这个结构体的详细解释:
成员解释
env:指向stCoRoutineEnv_t结构体的指针,表示协程所在的环境。
pfn_co_routine_t pfn:指向协程体的函数指针,表示协程的主体逻辑。
*void arg:协程的参数,传递给协程体的参数。
coctx_t ctx:协程的上下文,用于保存协程的执行状态。
cStart, cEnd, cIsMain, cEnableSysHook, cIsShareStack:这些是字符类型的标志位,用于标记协程的特性,例如是否是主协程、是否启用系统钩子等。
*void pvEnv:未明确指定的环境指针,可能用于特定的协程环境。
stStackMem_t stack_mem*:指向stStackMem_t结构体的指针,用于管理协程的堆栈内存。运行是栈的结构,libco提供了两种方式,一个是每个协程拥有一个独立的栈,默认分配128KB空间,缺点是每个协程可能只用到了1KB不到,碎片较多。还有一种是共享栈模式,需要我们在创建协程的时候在Co_create中指定第二个参数,这种方法是多个协程共用一个栈,但是在协程切换的时候需要拷贝已使用的栈空间。
char stack_sp, unsigned int save_size, char save_buffer**:用于保存堆栈冲突时的堆栈缓冲区信息。
stCoSpec_t aSpec[1024]:一个数组,用于存储协程的特殊变量或特性。
结构体的使用
stCoRoutine_t结构体用于表示一个协程实体,包含了协程的环境、执行体、参数、上下文、标志位、堆栈管理以及特殊变量等信息。开发者可以使用这个结构体来创建、管理和调度协程,以实现并发执行。
3 Liboc协程生产者消费者例子
你提供的代码展示了如何使用协程(co-routine)在生产者-消费者模式下进行任务处理。下面是对代码的详细解释:1. 头文件包含:* #include <unistd.h>:POSIX标准的系统调用库。* #include <stdio.h>:标准输入输出库。* #include <stdlib.h>:标准库。* #include <queue>:C++标准库中的队列。* "co_routine.h":假设这是你自定义的协程库头文件。2. 任务结构体:struct stTask_t{int id;};stTask_t结构体用于表示任务,其中包含一个任务ID。3. 环境结构体:struct stEnv_t{stCoCond_t* cond;queue<stTask_t*> task_queue;};stEnv_t结构体用于表示协程环境,包含一个条件变量cond和一个任务队列task_queue。4. 生产者协程:void* Producer(void* args){co_enable_hook_sys();stEnv_t* env= (stEnv_t*)args;int id = 0;while (true){stTask_t* task = (stTask_t*)calloc(1, sizeof(stTask_t));task->id = id++;env->task_queue.push(task);printf("%s:%d produce task %d\n", __func__, __LINE__, task->id);co_cond_signal(env->cond);poll(NULL, 0, 1000);}return NULL;}Producer函数是一个生产者协程,它不断地创建任务并将其放入任务队列中,然后通知消费者协程有新任务到达。co_enable_hook_sys()用于启用协程系统调用hook,co_cond_signal(env->cond)用于通知消费者协程有新任务到达,poll(NULL, 0, 1000)用于阻塞一段时间(1秒)。5. 消费者协程:void* Consumer(void* args){co_enable_hook_sys();stEnv_t* env = (stEnv_t*)args;while (true){if (env->task_queue.empty()){co_cond_timedwait(env->cond, -1);continue;}stTask_t* task = env->task_queue.front();env->task_queue.pop();printf("%s:%d consume task %d\n", __func__, __LINE__, task->id);free(task);}return NULL;}Consumer函数是一个消费者协程,它不断地从任务队列中取出任务并处理。co_cond_timedwait(env->cond, -1)用于在没有任务时等待,直到生产者协程通知有新任务到达。6. 主函数:int main(){stEnv_t* env = new stEnv_t;env->cond = co_cond_alloc();stCoRoutine_t* consumer_routine;co_create(&consumer_routine, NULL, Consumer, env);co_resume(consumer_routine);stCoRoutine_t* producer_routine;co_create(&producer_routine, NULL, Producer, env);co_resume(producer_routine);co_eventloop(co_get_epoll_ct(), NULL, NULL);return 0;}在main函数中,首先创建了一个协程环境env,然后分别创建了生产者和消费者协程,并使用co_resume函数启动它们。最后,使用co_eventloop函数进入事件循环,等待协程的执行完成。说明* 协程创建和启动:使用co_create和co_resume函数创建和启动协程。* 条件变量使用:生产者和消费者协程通过条件变量进行通信,生产者通知消费者有新任务到达。* 任务队列:使用C++标准库中的queue来管理任务队列。* 事件循环:使用co_eventloop函数进入事件循环,等待协程的执行完成。这个示例展示了如何使用协程实现生产者-消费者模式,通过条件变量和任务队列实现协程之间的通信和任务处理。
4 Liboc协程生产者消费者, 为什么队列不需要上锁?
在多线程环境中,使用队列而不需要显式上锁(锁定)主要依赖于以下几个因素:
-
线程局部存储(Thread Local Storage, TLS):在你的代码中,使用了__thread关键字来确保每个线程都有自己独立的gCoEnvPerThread变量副本。这意味着每个线程在访问stEnv_t结构体中的task_queue时,都会访问自己独立的队列副本,而不是共享的全局队列。因此,不需要显式上锁,因为每个线程只会影响自己的队列副本。
-
协程的特性:协程(co-routine)是一种轻量级的线程切换机制,它允许在多个函数之间切换执行,而不需要创建和销毁线程。协程的上下文切换开销比线程要小得多,因此可以更频繁地进行切换。由于协程的特性,协程之间的通信通常不需要复杂的锁机制,因为协程的上下文切换是快速且高效的。
-
条件变量的使用:在生产者-消费者模式中,条件变量用于在生产者和消费者之间进行通信。当生产者向条件变量发送信号时,消费者会在等待中被唤醒,从而避免了在队列上进行显式的锁定。条件变量的使用确保了生产者和消费者之间的同步,而不需要显式的锁机制。
-
队列的线程安全性:C++标准库中的queue默认不是线程安全的,但在你的代码中,task_queue是stEnv_t结构体的一部分,而stEnv_t是每个线程的独立副本。因此,每个线程对task_queue的操作都是线程安全的,不需要额外的锁机制。
然而,虽然在你的代码中没有显式上锁,但在多线程环境中,仍然需要确保每个线程对共享资源的访问是线程安全的。在上述代码中,这通过__thread关键字和stEnv_t结构体的独立副本实现。
总之,使用__thread关键字和条件变量,结合协程的特性,使得在生产者-消费者模式中可以避免显式上锁,从而提高了代码的效率和可读性。
5. 两个协程访问资源不需要加队列吗
在你的代码中,虽然两个协程(生产者和消费者)访问task_queue时没有显式地使用锁(如std::mutex),但这并不意味着没有进行资源保护。这里的关键在于__thread关键字和协程的特性。
- _THREAD关键字的作用
__thread关键字用于在多线程环境中为每个线程提供独立的变量副本。因此,当你在全局作用域中定义gCoEnvPerThread变量时,__thread关键字确保了每个线程都有自己独立的stEnv_t结构体副本,包括其中的task_queue。这意味着每个线程在访问task_queue时,都会访问自己独立的队列副本,而不是共享的全局队列。因此,不需要显式上锁,因为每个线程只会影响自己的队列副本。
- 协程的特性
协程是一种轻量级的线程切换机制,它允许在多个函数之间切换执行,而不需要创建和销毁线程。协程的上下文切换开销比线程要小得多,因此可以更频繁地进行切换。由于协程的特性,协程之间的通信通常不需要复杂的锁机制,因为协程的上下文切换是快速且高效的。
- 条件变量的使用
在生产者-消费者模式中,条件变量用于在生产者和消费者之间进行通信。当生产者向条件变量发送信号时,消费者会在等待中被唤醒,从而避免了在队列上进行显式的锁定。条件变量的使用确保了生产者和消费者之间的同步,而不需要显式的锁机制。
- 线程安全性
虽然在你的代码中没有显式上锁,但在多线程环境中,仍然需要确保每个线程对共享资源的访问是线程安全的。在上述代码中,这通过__thread关键字和stEnv_t结构体的独立副本实现。每个线程对task_queue的操作都是线程安全的,因为每个线程都有自己独立的stEnv_t结构体副本。
总之,使用__thread关键字和条件变量,结合协程的特性,使得在生产者-消费者模式中可以避免显式上锁,从而提高了代码的效率和可读性。然而,这并不意味着没有进行资源保护,而是通过__thread关键字和协程的特性实现了线程安全。
5. 参考
https://blog.csdn.net/weixin_43705457/article/details/106863859
相关文章:
[libos源码学习 1] Liboc协程生产者消费者举例
文章目录 1. CoRoutineEnv_t结构体用于管理协程环境 3 Liboc协程生产者消费者例子4 Liboc协程生产者消费者, 为什么队列不需要上锁?5. 两个协程访问资源不需要加队列吗5. 参考 1. CoRoutineEnv_t结构体用于管理协程环境 struct stCoRoutineEnv_t { stCo…...

Python OpenCV 图像改变
更改图像数据 通过 改像素点 或者 切片的区域 import cv2 import numpy as np img cv2.imread("image.jpg") print(img[3,5]) # 显示某位置(行3列5)的像素值( 如 [53 34 29] 它是有三通道 B G R 组成) img[3,5] (0,0,255) # 更改该位置的像素…...
k8s按需创建 PV和创建与使用 PVC
在 Kubernetes 中,PersistentVolume(PV)和 PersistentVolumeClaim(PVC)用于管理存储资源。PV 是集群中的存储资源,而 PVC 是 Pod 请求 PV 的方式。按需创建 PV 通常使用 StorageClass 实现动态存储分配&…...

揭秘云计算 | 2、业务需求推动IT发展
揭秘云计算 | 1、云从哪里来?-CSDN博客https://blog.csdn.net/Ultipa/article/details/143430941?spm1001.2014.3001.5502 书接上文: 过去几十年间IT行业从大型主机过渡到客户端/服务器,再过渡到现如今的万物互联,IT可把控的资…...

【系统面试篇】进程与线程类(2)(笔记)——进程调度、中断、异常、用户态、核心态
目录 一、相关面试题 1. 进程的调度算法有哪些? 调度原则 (1)先来先服务调度算法 (2)最短作业优先调度算法 (3)高响应比优先调度算法 (4)时间片轮转调度算法 &am…...

基于MySQL的企业专利数据高效查询与统计实现
背景 在进行产业链/产业评估工作时,我们需要对企业的专利进行评估,其中一个重要指标是统计企业每一年的专利数量。本文基于MySQL数据库,通过公司名称查询该公司每年的专利数,实现了高效的专利数据统计。 流程 项目流程概述如下&…...

热成像手机VS传统热成像仪:AORO A23为何更胜一筹?
热成像技术作为一种非接触式测温方法,广泛应用于石油化工巡检、电力巡检、应急救援、医疗、安防等“危、急、特”场景。提及热成像设备,人们往往会首先想到价格高昂、操作复杂且便携性有限的热成像仪。但是,随着技术的不断进步,市…...

Spring IoC——依赖注入
1. 依赖注入的介绍 DI,也就是依赖注入,在容器中建立的 bean (对象)与 bean 之间是有依赖关系的,如果直接把对象存在 IoC 容器中,那么就都是一个独立的对象,通过建立他们的依赖关系,…...

Linux 中,flock 对文件加锁
在Linux中,flock是一个用于对文件加锁的实用程序,它可以帮助协调多个进程对同一个文件的访问,避免出现数据不一致或冲突等问题。以下是对flock的详细介绍: 基本原理 flock通过在文件上设置锁来控制多个进程对该文件的并发访问。…...

CentOS下载ISO镜像的方法
步骤 1:访问CentOS官方网站 首先,打开浏览器,输入CentOS的官方网站地址:Download 在网站上找到ISO镜像的下载链接,通常位于“Downloads”或类似的页面上。 选择所需的CentOS版本和架构(如x86_64…...

Node.js 入门指南:从零开始构建全栈应用
🌈个人主页:前端青山 🔥系列专栏:node.js篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来node.js篇专栏内容:node.js-入门指南:从零开始构建全栈应用 前言 大家好,我是青山。作…...

MYSQL 真实高并发下的死锁
https://pan.baidu.com/s/1nM3VQdbkNZhnK-wWboEYxA?pwdvwu6 下面是风控更新语句 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 2023-08-04 01:00:10 140188779017984 *** (1) TRANSACTION: TRANSACTION 895271870, ACTIVE 0 sec starting …...

Zookeeper 简介 | 特点 | 数据存储
1、简介 zk就是一个分布式文件系统,不过存储数据的量极小。 1. zookeeper是一个为分布式应用程序提供的一个分布式开源协调服务框架。是Google的Chubby的一个开源实现,是Hadoop和Hbase的重要组件。主要用于解决分布式集群中应用系统的一致性问题。 2. 提…...

设计模式之结构型模式---装饰器模式
目录 1.概述2.类图3.应用场景及优缺点3.1 应用场景3.2 优缺点3.2.1 优点3.2.2 缺点 4.实现4.1 案例类图4.2 代码实现4.2.1 定义抽象构建角色4.2.2 定义具体构建角色4.2.3 定义抽象装饰器角色4.2.4 定义具体装饰角色4.2.5 装饰器模式的使用 1.概述 装饰器模式是指在不改变现有对…...
Android Pair
Pair在Android中是一种轻量级的工具类,并不是严格意义上的数据结构。 数据结构是一组有组织的方式来存储和管理数据的方式,如数组、链表、栈、队列、树、图等,它们有自己的特性和操作规则。而Pair更像是一个简单的封装,用于在需要…...

华为荣耀曲面屏手机下面空白部分设置颜色的方法
荣耀部分机型下面有一块空白区域,如下图红框部分 设置这部分的颜色需要在themes.xml里面设置navigationBarColor属性 <item name"android:navigationBarColor">android:color/white</item>...

《C#语法一篇通》,有20万字,需8MB字节,宜48小时阅读,没准会继续完善
本文摘录了C#语法的主要内容,接近20万字。 所有鸡汤的味道都等于马尿! 如果你相信任何所谓的鸡汤文章,智商堪忧。 计算机语言没有”好不好“之说,骗子才会告诉你哪个语言好,学好任何一本基础语言(C&#…...

嵌入式硬件工程师的职业发展规划
嵌入式硬件工程师可以按照以下阶段进行职业发展规划: 1. **初级阶段(1-3 年) ** - **技术学习与积累**: **电路基础强化**: 深入学习模拟电路和数字电路知识,能够熟练分析和设计基本的电路,…...

QT for android 问题总结(QT 5.15.2)
1.配置好的sdk,显示设置失败 Android SDK Command-line Tools run. Android Platform-Tools installed. Command-line Tools (latest) 版本过高导致报错 ,下载一个低版本的latest ,替换掉之前latest中的文件。即可,latest 路径如…...
PyTorch实战-手写数字识别-MLP模型
1 需求 包懂,40分钟掌握PyTorch深度学习框架,对应神经网络算法理论逐行讲解用PyTorch实现图像分类代码_哔哩哔哩_bilibili 10分钟入门神经网络 PyTorch 手写数字识别_哔哩哔哩_bilibili pytorch tutorial: PyTorch 手写数字识别 教程代码 从零设计并训…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...