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

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...