当前位置: 首页 > news >正文

[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协程生产者消费者, 为什么队列不需要上锁?

在多线程环境中,使用队列而不需要显式上锁(锁定)主要依赖于以下几个因素:

  1. 线程局部存储(Thread Local Storage, TLS):在你的代码中,使用了__thread关键字来确保每个线程都有自己独立的gCoEnvPerThread变量副本。这意味着每个线程在访问stEnv_t结构体中的task_queue时,都会访问自己独立的队列副本,而不是共享的全局队列。因此,不需要显式上锁,因为每个线程只会影响自己的队列副本。

  2. 协程的特性:协程(co-routine)是一种轻量级的线程切换机制,它允许在多个函数之间切换执行,而不需要创建和销毁线程。协程的上下文切换开销比线程要小得多,因此可以更频繁地进行切换。由于协程的特性,协程之间的通信通常不需要复杂的锁机制,因为协程的上下文切换是快速且高效的。

  3. 条件变量的使用:在生产者-消费者模式中,条件变量用于在生产者和消费者之间进行通信。当生产者向条件变量发送信号时,消费者会在等待中被唤醒,从而避免了在队列上进行显式的锁定。条件变量的使用确保了生产者和消费者之间的同步,而不需要显式的锁机制。

  4. 队列的线程安全性: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协程生产者消费者&#xff0c; 为什么队列不需要上锁&#xff1f;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 中&#xff0c;PersistentVolume&#xff08;PV&#xff09;和 PersistentVolumeClaim&#xff08;PVC&#xff09;用于管理存储资源。PV 是集群中的存储资源&#xff0c;而 PVC 是 Pod 请求 PV 的方式。按需创建 PV 通常使用 StorageClass 实现动态存储分配&…...

揭秘云计算 | 2、业务需求推动IT发展

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

【系统面试篇】进程与线程类(2)(笔记)——进程调度、中断、异常、用户态、核心态

目录 一、相关面试题 1. 进程的调度算法有哪些&#xff1f; 调度原则 &#xff08;1&#xff09;先来先服务调度算法 &#xff08;2&#xff09;最短作业优先调度算法 &#xff08;3&#xff09;高响应比优先调度算法 &#xff08;4&#xff09;时间片轮转调度算法 &am…...

基于MySQL的企业专利数据高效查询与统计实现

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

热成像手机VS传统热成像仪:AORO A23为何更胜一筹?

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

Spring IoC——依赖注入

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

Linux 中,flock 对文件加锁

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

CentOS下载ISO镜像的方法

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

Node.js 入门指南:从零开始构建全栈应用

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

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就是一个分布式文件系统&#xff0c;不过存储数据的量极小。 1. zookeeper是一个为分布式应用程序提供的一个分布式开源协调服务框架。是Google的Chubby的一个开源实现&#xff0c;是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中是一种轻量级的工具类&#xff0c;并不是严格意义上的数据结构。 数据结构是一组有组织的方式来存储和管理数据的方式&#xff0c;如数组、链表、栈、队列、树、图等&#xff0c;它们有自己的特性和操作规则。而Pair更像是一个简单的封装&#xff0c;用于在需要…...

华为荣耀曲面屏手机下面空白部分设置颜色的方法

荣耀部分机型下面有一块空白区域&#xff0c;如下图红框部分 设置这部分的颜色需要在themes.xml里面设置navigationBarColor属性 <item name"android:navigationBarColor">android:color/white</item>...

《C#语法一篇通》,有20万字,需8MB字节,宜48小时阅读,没准会继续完善

本文摘录了C#语法的主要内容&#xff0c;接近20万字。 所有鸡汤的味道都等于马尿&#xff01; 如果你相信任何所谓的鸡汤文章&#xff0c;智商堪忧。 计算机语言没有”好不好“之说&#xff0c;骗子才会告诉你哪个语言好&#xff0c;学好任何一本基础语言&#xff08;C&#…...

嵌入式硬件工程师的职业发展规划

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

QT for android 问题总结(QT 5.15.2)

1.配置好的sdk&#xff0c;显示设置失败 Android SDK Command-line Tools run. Android Platform-Tools installed. Command-line Tools (latest) 版本过高导致报错 &#xff0c;下载一个低版本的latest &#xff0c;替换掉之前latest中的文件。即可&#xff0c;latest 路径如…...

PyTorch实战-手写数字识别-MLP模型

1 需求 包懂&#xff0c;40分钟掌握PyTorch深度学习框架&#xff0c;对应神经网络算法理论逐行讲解用PyTorch实现图像分类代码_哔哩哔哩_bilibili 10分钟入门神经网络 PyTorch 手写数字识别_哔哩哔哩_bilibili pytorch tutorial: PyTorch 手写数字识别 教程代码 从零设计并训…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了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; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

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 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...