嵌入式学习之系统编程(六)线程
目录
一、线程
(一)线程概念
(二)特征
(三)优缺点
二、线程与进程的区别(面问)
三、多线程程序设计步骤
四、线程的创建(相关函数)
1、pthread_create()
2、pthread_self()
3、扩展:strerror()
五、线程的退出
1、pthread_exit()
2、pthread_cancel()
六、线程的回收
(一)线程的回收机制
(二)线程回收相关函数 pthread_join()
(三)次线程的回收策略
七、线程的参数,返回值
八、分离属性
1、pthread_attr_init()
2、pthread_attr_destroy()
3、pthread_attr_setdetachstate()
4、pthread_deatch()
线程清理函数
5、pthread_cleanup_push()
6、pthread_cleanup_pop()
7、注:
8、示例
一、线程
(一)线程概念
1、线程是轻量级进程,一般是一个进程中的多个任务(一个进程可以有多个线程);
2、进程是系统中最小的资源分配单位;
线程是系统中最小的执行单位。
(二)特征
1、共享资源:
2、效率高 30%
3、三方库: pthread clone posix
4.、编写代码头文件: pthread.h
5、 编译代码加载库: gcc x.c -lpthread
扩:以lib开头 .so结尾的为动态库(在内核中),中间的pthread为库名。
(三)优缺点
优点:比多进程节省资源,可以共享变量。
缺点:1.线程和进程相比,稳定性,稍微差些
2.线程的调试gdb,相对麻烦些,因为并发
二、线程与进程的区别(面问)
1、资源:
(1)线程比进程多了共享资源;
(2)线程又具有部分私有资源;
(3)进程间只有私有资源没有共享资源。
2、空间:
(1)进程空间独立(写时复制),不能直接通信;
(2)线程可以共享空间(栈区默认不共享),可以直接通信。
3、不同点:
(1)创建开销不一样,进程创建需要3G空间,线程创建只需要8M,线程并发度高于进
程;
(2)进程变量不共享;
(3)进程切换(复杂)需要的缓存空间多,线程切换栈区,PC指针也发生变化;
(4)进程可申请到硬件资源;
4、稳定性差异:
(1)线程稳定性差(其中一个线程崩溃或严重异常,进程直接结束);
(2)项目任务复杂,用进程做;简单小任务,用线程做;
5、共同点:都能并发。
6、考虑用线程还是进程的两个准则:
(1)稳定性;
(2)看资源够不够用(够用用线程)。
三、多线程程序设计步骤
1、创建多线程(只要创建成功就启动了)
2、线程空间操作 (设计函数表达要干什么)
3、线程资源回收(栈区回收,默认进程结束栈区不释放)
注:
(1)进程中的第一个线程为主线程,主线程有且仅有一个,(主线程号和进程号一
致)其它次线程为平级关系;
(2)主线程不需创建,运行a.out就出来了,后调用函数创建其他空间,在进程空间新
开栈区(8M)。
四、线程的创建(相关函数)
1、pthread_create()
(1)函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
(2)功能:该函数可以创建指定的一个线程。
(3)参数:
thread 线程id,需要实现定义并由该函数返回。
attr 线程属性,一般是NULL,表示默认属性。
start_routine 指向指针函数的函数指针, 本质上是一个函数的名称即可。称为th 回调
函数,是线程的执行空间。
(4)返回值:成功 0;失败 错误码
(5)注意:
一次pthread_create执行只能创建一个线程。
每个进程至少有一个线程称为主线程。
主线程退出则所有创建的子线程都退出。
主线程必须有子线程同时运行才算多线程程序。
线程id是线程的唯一标识,是CPU维护的一组数字。
pstree 查看系统中多线程的对应关系。
多个子线程可以执行同一回调函数。
(6)示例
注:gcc编译命令:gcc 01pthread_create.c -lpthread
2、pthread_self()
(1)函数原型:pthread_t pthread_self(void);
(2)功能:获取当前线程的线程id(谁调就返回谁的id号)
(3)参数:无
(4) 返回值:成功 返回当前线程的线程id; 失败 -1
(5)用户层表示(ps命令)与内核层表示(数字大)不同,使用内核层表示
用户层查看(man ps ps-eLf)
(6)示例:
3、扩展:strerror()
(1)报错函数,用于线程报错(多为极端情况使用),也可调用perror()函数(大部分
情况使用)
(2)strerror(errno)中errno为错误码
五、线程的退出
1、pthread_exit()
自行退出 = =自杀 ==子线程自己退出
(1)函数原型:void pthread_exit(void *retval); exit return p;
(2)功能:子线程自行退出
(3)参数: retval 线程退出时候的返回状态,临死遗言。
(4)返回值:无
(5)示例:
2、pthread_cancel()
强制退出 ==他杀 ==主线程结束子线程
(1)函数原型:int pthread_cancel(pthread_t thread);
(2)功能:请求结束一个线程
(3)参数:thread 请求结束一个线程tid
(4)返回值:成功 0;失败 -1
(5)示例:
六、线程的回收
(一)线程的回收机制
1、不同与进程没有孤儿线程和僵尸线程;
2、主线程结束任意生成的子线程都会结束;
3、子线程的结束不会影响主线程的运行。
(二)线程回收相关函数 pthread_join()
1、函数原型:int pthread_join(pthread_t thread, void **retval);
2、功能:通过该函数可以将指定的线程资源回收(线程结束栈区默认没被释放),
该函数具有 阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。
3、 参数:thread 要回收的子线程tid
retval 要回收的子线程返回值/状态;ptread_exit(值);
二级指针改变指针的指向
4、返回值:成功 0; 失败 -1
5、扩展:Tcb块(线程控制块)
(进程中有10个线程,pcb块中就有10个tcb块)
6、次线程返回的不能为局部变量(解决办法:加static,malloc申请(+free()))
7、示例:
(1)pthread_join(NULL);
(2)void* ret;
pthread_join(tid,&ret);
8、练习:
(三)次线程的回收策略
1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收;
2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收;
3、如果子线程已知必须长时间运行则,不再回收其资源。
七、线程的参数,返回值
1、传参数整数示例:
传参目的:降低程序耦合性(传参>定义全局变量)
2、结构体示例:
#include <stdio.h> // 包含标准输入输出头文件
#include <stdlib.h> // 包含标准库头文件(含内存分配、atoi等函数)
#include <string.h> // 包含字符串操作头文件(如strcat、strlen等)
#include <unistd.h> // 包含UNIX系统相关头文件(此处可能未实际使用)
#include <pthread.h> // 包含POSIX线程头文件,用于多线程编程
typedef struct // 定义结构体类型PER
{
char name[50]; // 姓名,字符数组长度50
int age; // 年龄,整数类型
char addr[50]; // 地址,字符数组长度50
} PER; // 结构体别名PER
void* th(void* arg) // 线程函数,返回值和参数均为void指针
{
PER per = (PER*)arg; // 将参数强制转换为PER结构体指针
printf("th:%s %d %s\n", per->name, per->age, per->addr); // 打印线程中结构体信息
strcat(per->name, "1"); // 向姓名后拼接字符串"1"(修改原数据)
return per; // 返回结构体指针(作为线程返回值)
}
int main(int argc, char argv) // 主函数,argc为参数个数,argv为参数数组
{
PER per; // 定义PER结构体变量per
bzero(&per, sizeof(per)); // 用bzero函数将per内存块清零(初始化)
printf("pls name:"); // 提示输入姓名
fgets(per.name, sizeof(per.name), stdin); // 读取输入到name数组,含换行符
per.name[strlen(per.name)-1] = '\0'; // 删除末尾换行符(替换为字符串结束符)
char buf[5] = {0}; // 定义缓冲区,用于临时存储年龄输入
printf("pls age:"); // 提示输入年龄
fgets(buf, sizeof(buf), stdin); // 读取输入到buf(最多4个字符+终止符)
per.age = atoi(buf); // 将buf字符串转换为整数赋给age
printf("pls addr:"); // 提示输入地址
fgets(per.addr, sizeof(per.addr), stdin); // 读取输入到addr数组,含换行符
per.addr[strlen(per.addr)-1] = '\0'; // 删除addr末尾换行符
pthread_t tid; // 定义线程ID变量tid
pthread_create(&tid, NULL, th, &per); // 创建线程,传入per地址作为参数
void ret = NULL; // 定义线程返回值存储变量
pthread_join(tid, &ret); // 等待线程结束,获取返回值
// 打印主线程中获取的线程返回值(结构体信息)
printf("join ret :%s %d %s\n", ((PER)ret)->name, ((PER*)ret)->age, ((PER*)ret)->addr);
system("pause"); // 调用系统命令pause(Windows环境暂停程序)
return 0; // 主函数正常退出
}
八、分离属性
目的:线程消亡,自动回收空间。
1、pthread_attr_init()
(1)函数原型:int pthread_attr_init(pthread_attr_t *attr);
(2)功能:初始化一个attr的变量
(3)参数:attr,需要变量来接受初始值
(4)返回值:0 成功,非0 错误
2、pthread_attr_destroy()
(1)函数原型:int pthread_attr_destroy(pthread_attr_t *attr);
(2)功能:销毁attr变量
(3)参数:attr,属性变量
(4)返回值:0 成功,非0 错误
3、pthread_attr_setdetachstate()
(1)函数原型: int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
(2)功能:把一个线程设置成相应的属性
(3)参数:attr,属性变量,有init函数初始化他。
detachstate:有2个可选值,
PTHREAD_CREATE_DETACHED
4、pthread_deatch()
(1)函数原型:int pthread_deatch(pthread_t thread);
(2)功能:设置分离属性
(3)参数:线程id号,填自己的id
线程清理函数
5、pthread_cleanup_push()
(1)函数原型:void pthread_cleanup_push(void (*routine)(void *), void *arg);
(2)功能:注册一个线程清理函数
(3)参数:routine,线程清理函数的入口
arg,清理函数的参数。
(4)返回值:无
6、pthread_cleanup_pop()
(1)函数原型:void pthread_cleanup_pop(int execute);
(2)功能:调用清理函数
(3)参数:execute,非0 执行清理函数
0 ,不执行清理
(4)返回值:无
注:pthread_cleanup_push与pthread_cleanup_pop函数需成对出现;
7、注:
进程可以启动线程,线程也可启动进程(进程中的多线程变为单线程)
8、示例
(1)在循环中批量创建分离态线程,并统计成功创建的线程数量:
#include <pthread.h> // 包含POSIX线程头文件,用于多线程操作
#include <stdio.h> // 包含标准输入输出头文件
#include <stdlib.h> // 包含标准库头文件(含内存分配等函数)
#include <unistd.h> // 包含UNIX系统相关头文件(此处可能未实际使用)
void* th(void* arg) // 线程函数,返回值和参数均为void*指针
{
pthread_detach(pthread_self()); // 将当前线程设置为分离态,线程结束后系统自动回收资源
return NULL; // 线程执行完毕,返回空指针
}
int main(int argc, char** argv) // 主函数,argc为参数个数,argv为参数数组
{
int i = 0; // 循环计数器
pthread_t tid; // 存储线程ID的变量
for (i = 0; i < 50000; i++) // 循环创建50000个线程
{
int ret = pthread_create(&tid, NULL, th, NULL); // 创建线程,传入线程函数th,无参数
if (ret != 0) // 检查线程创建是否失败(非0表示失败)
{
break; // 失败则退出循环
}
printf("%d\n", i); // 打印当前创建的线程序号
}
system("pause"); // 调用系统命令pause(Windows环境暂停程序,防止退出)
return 0; // 主函数正常退出
}
(2)利用线程清理处理机制确保动态分配内存的正确释放:
#include <pthread.h> // 包含POSIX线程相关头文件(线程创建、清理等)
#include <stdio.h> // 包含标准输入输出头文件(printf等)
#include <stdlib.h> // 包含标准库头文件(malloc、free等内存操作)
#include <unistd.h> // 包含UNIX系统相关头文件(此处未实际使用)
#include <string.h> // 包含字符串操作头文件(strcpy等)
void clean(void arg) // 线程清理处理函数,参数为void指针
{
printf("this clean %s\n", (char*)arg); // 打印清理信息,输出传入的字符串
free(arg); // 释放动态分配的内存,避免泄漏
}
void* th(void* arg) // 线程执行函数,返回值和参数均为void指针
{
strcpy((char )arg, "hello world\n"); // 将字符串复制到传入的内存地址(修改arg指向的数据)
printf("th,strcpy over\n"); // 打印线程内操作完成的提示
return NULL; // 线程执行完毕,返回空指针
}
int main(int argc, char *argv) // 主函数,argc为参数个数,argv为参数数组
{
pthread_t tid; // 定义线程ID变量,用于存储创建的线程标识
char p = malloc(50); // 动态分配50字节内存,返回指针p指向该内存块
pthread_cleanup_push(clean, p); // 注册线程清理函数clean,参数为p(内存地址)
pthread_create(&tid, NULL, th, p); // 创建线程,传入线程函数th和参数p(内存地址)
pthread_join(tid, NULL); // 阻塞等待线程tid结束,回收其资源
printf("before pop\n"); // 打印提示,表示即将执行清理函数弹出操作
pthread_cleanup_pop(1); // 弹出清理函数,并执行(参数1表示执行清理函数)
system("pause"); // 调用系统命令pause(Windows环境暂停程序)
return 0; // 主函数正常退出
}
相关文章:

嵌入式学习之系统编程(六)线程
目录 一、线程 (一)线程概念 (二)特征 (三)优缺点 二、线程与进程的区别(面问) 三、多线程程序设计步骤 四、线程的创建(相关函数) 1、pthread_create…...
分布式常见概念
分布式常见概念 反向代理正向代理 vs 反向代理(对比理解名称)正向代理示意(“我去帮你拿数据”)反向代理示意(“你找我,我替你联系内部服务器”)为什么叫“反向”? API网关一、为什么…...
数据库的事务(Transaction)
在数据库中,事务(Transaction) 是保证数据操作一致性和完整性的核心机制。它通过一组原子性的操作单元,确保所有操作要么全部成功(提交),要么全部失败(回滚)。以下是数据…...

大语言模型 提示词的少样本案例的 演示选择与排序新突破
提示词中 演示示例的选择与排序 这篇论文《Rapid Selection and Ordering of In-Context Demonstrations via Prompt Embedding Clustering》聚焦于提升大语言模型(LLMs)在自适应上下文学习(ICL)场景中演示示例的选择与排序效率 一、论文要解决的问题 在上下文学习(ICL)…...

【算法篇】二分查找算法:基础篇
题目链接: 34.在排序数组中查找元素的第一个和最后一个位置 题目描述: 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返…...
Qtc++开发遇到的问题-按钮点击不管用?
我在设计自己的控件的时候,遇到了按钮点击不管用的问题,而且是有的自定义控件不管用,有的管用,有的一开始管用,多点几次就不管用了, 它是这样的,一个lineEdit和位于两侧的按钮,分别…...

重磅发布 | 复旦533页《大规模语言模型:从理论到实践(第2版)》(免费下载)
在人工智能浪潮席卷全球的今天,大语言模型正以前所未有的速度推动着科技进步和产业变革。从 ChatGPT 到各类行业应用,LLM 不仅重塑了人机交互的方式,更成为推动学术研究与产业创新的关键技术。 面对这一飞速演进的技术体系,如何系…...

智能体赋能效率,企业知识库沉淀价值:UMI企业智脑的双轮驱动!
智能体企业知识库:UMI企业智脑的核心功能与价值 在人工智能技术飞速发展的今天,企业智能化转型已经成为不可逆转的趋势。作为企业级AI智能体开发平台的佼佼者,优秘智能推出的UMI企业智脑,以其强大的智能体开发能力和全面的企业知…...
STM32CubeMX,arm-none-eabi-gcc简单试用
在windows下,为stm32系列单片机编程,keil有了免费的试用版,有很多开发板示例,给学习单片机编程带来很大的方便。 STM32CubeMX提供了stm32单片机的功能设置,在输出方式上给出了几种方式,有mdk(k…...
Spring AI(一)
Spring AI 官网 Spring AI 是一个用于 AI 工程的应用程序框架。其目标是将 Spring 生态系统设计原则(如可移植性和模块化设计)应用于 AI 领域,并将使用 POJO 作为应用程序的构建块推广到 AI 领域。 Spring AI 的核心是解决了 AI 集成的根本挑战:将您的企业数据和 API 与 A…...
Nacos适配GaussDB超详细部署流程
1部署openGauss 官方文档下载 https://support.huaweicloud.com/download_gaussdb/index.html 社区地址 安装包下载 本文主要是以部署轻量级为主要教程 1.1系统环境准备 操作系统选择 系统AARCH64X86-64openEuler√√CentOS7√Docker√√1.2软硬件安装环境 版本轻量版(单…...
vue-pure-admin动态路由无Layout实现解决方案
背景: 最近在使用vue-pure-admin开发后台项目的时候发现作者并没有动态路由的全屏无Layout实现方案。查询作者路由发现,作者只做了静态路由的无Layout方案,其它动态路由,作者在做整合的时候,都放进了 \ 下面的子路由&…...

vue项目 build时@vue-office/docx报错
我在打包vue项目时, 开始用的npm run build和cnpm run build,总是提示 vue-office/docx 错误,尝试过用cnpm重新安装node_modules几次都没用。类似下面的提示一直有。 Error: [commonjs--resolver] Failed to resolve entry for package "…...
卓力达蚀刻工艺:精密制造的跨行业赋能者
引言 蚀刻技术作为现代精密制造的核心工艺之一,通过化学或物理方法对金属材料进行选择性去除,实现微米级复杂结构的加工。南通卓力达凭借20余年技术积淀与全产业链布局,成为全球高端制造领域的重要支撑力量。本文将从蚀刻技术的多领域应用与…...
【大模型面试每日一题】Day 30:解释一下 FlashAttention 技术,并对比其与传统注意力在显存效率和计算性能上的差异。
【大模型面试每日一题】Day 30:解释一下 FlashAttention 技术,并对比其与传统注意力在显存效率和计算性能上的差异。 📌 题目重现 🌟🌟 面试官:解释一下 FlashAttention 技术,并对比其与传统注…...

#RabbitMQ# 消息队列入门
目录 一 MQ技术选型 1 运行rabbitmq 2 基本介绍 3 快速入门 1 交换机负责路由消息给队列 2 数据隔离 二 Java客户端 1 快速入门 2 WorkQueue 3 FanOut交换机 4 Direct交换机 5 Topic交换机 *6 声明队列交换机 1 在配置类当中声明 2 使用注解的方式指定 7 消息转…...
在promise中,多个then如何传值
在 JavaScript 中,Promise 的多个 .then() 是链式调用的,值可以通过返回值的方式,在多个 .then() 之间传递。这是 Promise 链式调用的核心机制。 基本原理:每个 then 接收上一个 then 的返回值 new Promise((resolve, reject) &g…...
TCP 三次握手过程详解
TCP 三次握手过程详解 一、TCP握手基础概念 1.1 什么是TCP握手 TCP三次握手是传输控制协议(Transmission Control Protocol)在建立连接时的标准过程,目的是确保通信双方具备可靠的双向通信能力。 关键结论:三次握手的本质是通过序列号同步和能力协商建立可靠的逻辑连接。 …...

EPT(Efficient Prompt Tuning)方法,旨在解决提示调优(Prompt Tuning)中效率与准确性平衡和跨任务一致性的问题
EPT(Efficient Prompt Tuning)方法,旨在解决提示调优(Prompt Tuning)中效率与准确性平衡和跨任务一致性的问题 一、核心原理:分解提示与多空间投影 1. 提示分解:用低秩矩阵压缩长提示 传统问题: 长提示(如100个token)精度高但训练慢,短提示(如20个token)速度快但…...

云原生安全核心:云安全责任共担模型(Shared Responsibility Model)详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 1. 基础概念 什么是云安全责任共担模型? 云安全责任共担模型(Shared Responsibility Model, SRM)是云服务提供商&…...

go并发与锁之sync.Mutex入门
sync.Mutex 原理:一个共享的变量,哪个线程握到了,哪个线程可以执行代码 功能:一个性能不错的悲观锁,使用方式和Java的ReentrantLock很像,就是手动Lock,手动UnLock。 使用例子: v…...

[Java恶补day8] 3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...

LabVIEW教学用开发平台
一、培训目标 基础编程:掌握 LabVIEW 数据类型、程序结构、子 VI 设计与调试技巧。 硬件通信:精通 RS-232/485、TCP/IP、Modbus、PLC 等工业通信协议及实现。 高级设计模式:熟练运用状态机、生产者 - 消费者模式构建复杂测控系统。 项目实…...

Package Size Comparison – 6 Leads
Package Size Comparison 6 LeadsTSOP SOT SM SMT SOT23 SC-74 SC-59 SC-88 SOT363 US6 UMT6 SC-70 SOT563 ES EMT SC-75-6...

python打卡day38
Dataset和DataLoader 知识点回顾: Dataset类的__getitem__和__len__方法(本质是python的特殊方法)Dataloader类minist手写数据集的了解 作业:了解下cifar数据集,尝试获取其中一张图片 在遇到大规模数据集时,…...

vLLM 核心技术 PagedAttention 原理详解
本文是 vLLM 系列文章的第二篇,介绍 vLLM 核心技术 PagedAttention 的设计理念与实现机制。 vLLM PagedAttention 论文精读视频可以在这里观看:https://www.bilibili.com/video/BV1GWjjzfE1b 往期文章: vLLM 快速部署指南 1 引言…...
rpm安装jenkins-2.452
rpm安装jenkins-2.452 一、下载和安装 1、Jenkins下载 版本2.452可用windows下载: https://mirrors.jenkins-ci.org/redhat-stable/jenkins-2.452.4-1.1.noarch.rpm 其他版本 wget https://pkg.jenkins.io/redhat-stable/jenkins-2.440.3-1.1.noarch.rpm 2、jenkins安装 $r…...

《软件工程》第 2 章 -UML 与 RUP 统一过程
在软件工程领域,UML(统一建模语言)与 RUP(统一过程)是进行面向对象软件开发的重要工具和方法。接下来,我们将深入探讨第 2 章的内容,通过案例和代码,帮助大家理解和掌握相关知识。 …...

(转)Docker与K8S的区别
1 定义角度 Docker是一种开放源码的应用容器引擎,允许开发人员将其应用和依赖包打包成可移植的容器/镜像中;然后,发布到任何流行的 Linux 或 Windows 机器上,也能实现虚拟化。该容器完全使用沙箱机制,彼此之间没有任何…...
服务器数据迁移
写在前面:为满足业务需求,我们采购了一台新的高性能服务器,现在想把旧服务器中的用户文件以及conda环境等迁移到新服务器中去。为了保证迁移过程尽可能不出错,并且迁移后新的服务器可以直接使用,以下方案提供一个稳健、…...