纯C无操作系统轻量协程库Protothread使用记录

文章目录

  • 目的
  • 源码说明
  • 使用演示
  • 总结

目的

在单片机开发中很多时候都是无操作系统环境,这时候如果要实现异步操作,并且流程逻辑比较复杂时处理起来会稍稍麻烦。这时候可以试试 Protothread 这个协程库。

官网: https://dunkels.com/adam/pt/

Protothreads are extremely lightweight stackless threads designed for severely memory constrained systems, such as small embedded systems or wireless sensor network nodes. Protothreads provide linear code execution for event-driven systems implemented in C. Protothreads can be used with or without an underlying operating system to provide blocking event-handlers. Protothreads provide sequential flow of control without complex state machines or full multi-threading.


Protothreads是为内存严重受限的系统(如小型嵌入式系统或无线传感器网络节点)设计的极为轻量级的无堆栈线程。协议线程为在C中实现的事件驱动系统提供线性代码执行。协议线程可以与底层操作系统一起使用,也可以不与底层操作体系一起使用,以提供阻塞事件处理程序。原线程提供顺序控制流,而无需复杂的状态机或完整的多线程。

这篇文章主要是我自己使用入门记录。具体是实现原理细节等可以参考官网的文档或是下面文章:
《一个“蝇量级” C 语言协程库》https://coolshell.cn/articles/10975.html

源码说明

从官网下载Protothread库解压后里面就包含了源码、例程和文档:
在这里插入图片描述
在这里插入图片描述

整个库总共就五个头文件:

  • pt.h 协程库用户接口;
  • lc.h 用来选择具体协程的实现方式(默认为 lc-switch.h ,可以手动在这里更改);
  • lc-switch.h 使用 C语言 switch/case 语法实现的协程(使用该方式时协程函数中不能使用 switch/case ,可能会冲突);
  • lc-addrlabels.h 使用 gcc label 特性实现的协程(这个依赖GCC编译器);
  • pt-sem.h 信号量实现;

pt.h 中几个数据接口和接口如下:

// 协程控制数据结构
struct pt {lc_t lc;
};// lc-switch.h中lc_t原型为typedef unsigned short lc_t;
// lc-addrlabels.h中lc_t原型为typedef void * lc_t;// 以下是协程调度过程中的一些返回状态
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED  2
#define PT_ENDED   3PT_INIT(pt) // 初始化控制数据结构(设置lc=0)PT_THREAD(name_args) // 声明一个协程的函数(这个用不用无所谓,官方的例程有时候也没用)
PT_BEGIN(pt) // 协程入口
PT_END(pt) // 协程出口PT_WAIT_UNTIL(pt, condition) // 等待condition为真向下运行,否则跳出当前协程
PT_WAIT_WHILE(pt, cond) // 和PT_WAIT_UNTIL相反,当cond为假向下运行,否则跳出当前协程PT_WAIT_THREAD(pt, thread) // 等待子协程thread调度完成
PT_SPAWN(pt, child, thread) // 启动子协程thread,并等待其完成。child是子协程的ptPT_RESTART(pt) // 重置协程
PT_EXIT(pt) // 退出协程PT_SCHEDULE(f) // 调度一个协程,如果协程还在运行则返回值非0,如果协程退出则返回值为0
PT_YIELD(pt) // 主动出让协程
PT_YIELD_UNTIL(pt, cond) // 等待cond为真向下运行,否则出让当前协程

pt-sem.h 中几个数据接口和接口如下:

// 信号量数据结构
struct pt_sem {unsigned int count;
};PT_SEM_INIT(s, c) // 初始化信号量值等于c
PT_SEM_WAIT(pt, s) // 等待信号量可用(>0),向下运行并消耗一个信号量
PT_SEM_SIGNAL(pt, s) // 给出一个信号量

使用演示

下面是个最简单的演示:
在这里插入图片描述

用上信号量的话上面代码可以改写成下面这样:

#include <stdio.h>
#include "pt-sem.h"static time_t pretime = 0, nowstamp;// 以下为信号量
static struct pt_sem sem1;
// 以下为协程控制数据
static struct pt pt1;
// 以下为协程函数
static PT_THREAD(protothread1(struct pt *pt)) {PT_BEGIN(pt); // 协程入口printf("Protothread1 begin\n\n");while(1) {PT_SEM_WAIT(pt, &sem1); // 等待信号量可用,并消耗信号量time(&nowstamp);printf("Protothread1 running, current time is %s\n", ctime(&nowstamp));}PT_END(pt); // 协程出口
}int main(void) {time(&pretime);PT_INIT(&pt1); // 初始化协程控制数据结构PT_SEM_INIT(&sem1, 0); // 初始化信号量while(1) {protothread1(&pt1); // 运行协程// 以下代码每2s给出一个semtime(&nowstamp);if((nowstamp - pretime) >= 2) {pretime = nowstamp;PT_SEM_SIGNAL(&pt1, &sem1); // 给出信号量}}
}

在这里插入图片描述

下面是一个协程间调用的演示:

#include <stdio.h>
#include "pt.h"static PT_THREAD(childpt(struct pt *pt)) {static int counter = 4; // 使用函数内部静态变量保存状态PT_BEGIN(pt); // 协程入口printf("childpt begin\n\n");while(counter--) {printf("childpt running, counter = %d\n\n", counter);PT_YIELD(pt); // 主动出让CPUprintf("childpt resume run\n\n");}printf("childpt end\n\n");PT_END(pt); // 协程出口
}static PT_THREAD(parentpt(struct pt *pt)) {static struct pt child;PT_BEGIN(pt); // 协程入口printf("parentpt begin\n\n");PT_SPAWN(pt, &child, childpt(&child)); // 调度子协程直至运行结束printf("parentpt end\n\n");PT_END(pt); // 协程出口
}int main(void) {static struct pt parant;PT_INIT(&parant); // 初始化协程控制数据结构while(PT_SCHEDULE(parentpt(&parant))); // 调度父协程直至运行结束while(1);
}

在这里插入图片描述

总结

Protothread使用起来比较简单,当然功能也比较简单。另外使用时还有一定的限制,比如使用默认实现时不能在协程中使用 switch/case ,需要在协程中使用静态变量来保存相关数据等。

如果上了操作系统的话,Protothread这种协程相对来说意义一般,但是对于没有操作系统的单片机开发这些来说Protothread就非常好用了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入剖析:Kafka流数据处理引擎的核心面试问题解析75问(5.7万字参考答案)

Kafka 是一款开源的分布式流处理平台&#xff0c;被广泛应用于构建实时数据管道、日志聚合、事件驱动的架构等场景。本文将深入探究 Kafka 的基本原理、特点以及其在实际应用中的价值和作用。 Kafka 的基本原理是建立在发布-订阅模式之上的。生产者将消息发布到主题&#xff08…

37-WEB漏洞-反序列化之PHPJAVA全解(上)

WEB漏洞-反序列化之PHP&JAVA全解&#xff08;上&#xff09; 一、PHP 反序列化原理二、案例演示2.1、无类测试2.1.1、本地2.1.2、CTF 反序列化小真题2.1.3、CTF 反序列化类似题 2.2、有类魔术方法触发2.2.1、本地2.2.2、网鼎杯 2020 青龙大真题 三、参考资料 一、PHP 反序列…

SpringMVC(八)处理AJAX请求

一、处理AJAX之准备工作: 首先我们创建一个新的工程: 我们将pom.xml复制过来: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-in…

【项目日记(三)】内存池的整体框架设计

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

MES管理系统为何成为汽配企业的刚需

随着经济全球化、产品定制化及安全法规的严格化&#xff0c;汽配企业的经营环境变得越来越复杂。中国劳动力资源和原辅料成本的持续上升&#xff0c;导致行业利润率不断下滑。为了应对这些挑战&#xff0c;汽配企业需要引入一种精益制造和管理的工具&#xff0c;而MES管理系统正…

四款通用组织架构图模板-一键高清导出

组织架构图作为一种直观的图形化工具&#xff0c;能够帮助我们更好地理解和规划组织结构&#xff0c;提高工作效率。今天&#xff0c;我们就为大家带来四款通用组织架构图模板&#xff0c;让你一键高清导出&#xff0c;轻松搞定组织架构设计&#xff01; 第一款&#xff1a;某基…

Excel乱码?教你3个简单解决方法!

“我在编辑一个文件时&#xff0c;Excel突然就乱码了&#xff0c;怎么会这样呢&#xff1f;这个文件对我来说是比较重要的&#xff01;有什么方法可以快速解决吗&#xff1f;” 在处理Excel文件时&#xff0c;我们有时会遇到乱码问题。乱码不仅影响数据的可读性&#xff0c;还可…

PySide6/PyQt6中Qt窗口标志/窗口属性汇总,如何正确的设置窗口标志/窗口属性

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📚 窗口标志汇总📚 窗口属性汇总📝 使用方法📝 注意事项⚓️ 相关链接 ⚓️📖 介绍 📖 在Qt框架中,窗口标志(window flags)是用于控制窗口的各种属性和行为的强大工具。它们通过设置窗口的属性,如边框…

vue3中Fragment特性的一个bug,需要留意的注意事项

vue3中的Fragment 模版碎片特性是什么&#xff0c;简单的理解就是template模板代码不在像vue2中那样必须在根节点在包裹一层节点了。 vue2写法 <template><div><h1>标题</h1><p>正文内容</p></div> </template>vue3写法 &l…

【OCR项目】之用HALCON的深度学习工具进行文字识别,并导出到C++调用

前言 HALCON是一个强大的机器视觉工具&#xff0c;包含了2D&#xff0c;3D图像各种算子&#xff0c;以及各种任务的深度学习工具&#xff0c;包括目标检测&#xff0c;实例分割&#xff0c;文字识别等。 这次从实际生产的角度&#xff0c;来分享一下如何用HALCON进行文字识别…

Redis(01)——常用指令

基础指令 select 数字&#xff1a;切换到其他数据库flushdb&#xff1a;清空当前数据库flushall&#xff1a;清空所有数据库dbsize&#xff1a;查看数据库大小exists key1[key2 …]&#xff1a;判断当前的key是否存在keys *&#xff1a;查看所有的keyexpire key 时间&#xff…

【数据结构与算法】1.数据结构绪论

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

EOCR电机保护器带煤电厂的具体应用

EOCR系列电动机智能保护器在煤电厂中有广泛的应用。这些保护器具有齐全的保护功能、直观的测量参数、快速的反应灵敏度、可靠的行动以及与上位机通讯构成远程监控的能力&#xff0c;使其成为理想的低压电动机保护及远程监控产品。 在煤电厂中&#xff0c;电动机保护器需要具备过…

【Linux多线程】生产者消费者模型

目录 生产者消费者模型 1. 生产者消费者模式的概念 2. 生产者消费者模型优点 ​编辑3. 生产者消费者模型的特点 基于BlockingQueue&#xff08;阻塞队列&#xff09;的生产者消费者模型 1.BlockingQueue 2. 使用CSTL中的queue来模拟实现阻塞队列 3. 基于任务的生产者消费…

三天吃透Java集合面试八股文

内容摘自我的学习网站&#xff1a;topjavaer.cn 常见的集合有哪些&#xff1f; Java集合类主要由两个接口Collection和Map派生出来的&#xff0c;Collection有三个子接口&#xff1a;List、Set、Queue。 Java集合框架图如下&#xff1a; List代表了有序可重复集合&#xff0c…

Modbus网关BL101 既实现Modbus转MQTT,还能当串口服务器使用

随着工业4.0的迅猛发展&#xff0c;人们深刻认识到在工业生产和生活中&#xff0c;实时、可靠、安全的数据传输至关重要。在此背景下&#xff0c;高性能的工业电力数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于工业自动化系统、远程监控和物联网应用应用环境…

Linux第34步_TF-A移植的第2步_修改设备树和tf-a.tsv

在虚拟机中&#xff0c;使用VSCode打开linux /atk-mp1/atk-mp1/my-tfa/目录下tf-a.code-workspace”&#xff1b; 找到“tf-a-stm32mp-2.2.r1/fdts”目录&#xff0c;就是设备树文件所在的目录。 见下图&#xff1a; 一、修改“stm32mp157d-atk.dts” 修改后&#xff0c;见下…

课题学习(十九)----Allan方差:陀螺仪噪声分析

一、介绍 Allan方差是一种分析时域数据序列的方法&#xff0c;用于测量振荡器的频率稳定性。该方法还可用于确定系统中作为平均时间函数的本征噪声。该方法易于计算和理解&#xff0c;是目前最流行的识别和量化惯性传感器数据中存在的不同噪声项的方法之一。该方法的结果与适用…

Window安装Python和开发Pycharm

准备&#xff1a; 1&#xff1a;安装Python环境 https://www.python.org/downloads/windows/ 2: 下载Pycharm https://www.jetbrains.com/pycharm/download/other.html