当前位置: 首页 > 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 手写数字识别 教程代码 从零设计并训…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...