嵌入式学习之系统编程(六)线程

news2025/5/29 15:19:22

目录

一、线程

(一)线程概念

(二)特征

(三)优缺点

二、线程与进程的区别(面问)

三、多线程程序设计步骤

四、线程的创建(相关函数)

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;                     // 主函数正常退出
}

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

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

相关文章

大语言模型 提示词的少样本案例的 演示选择与排序新突破

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

【算法篇】二分查找算法:基础篇

题目链接&#xff1a; 34.在排序数组中查找元素的第一个和最后一个位置 题目描述&#xff1a; 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返…

重磅发布 | 复旦533页《大规模语言模型:从理论到实践(第2版)》(免费下载)

在人工智能浪潮席卷全球的今天&#xff0c;大语言模型正以前所未有的速度推动着科技进步和产业变革。从 ChatGPT 到各类行业应用&#xff0c;LLM 不仅重塑了人机交互的方式&#xff0c;更成为推动学术研究与产业创新的关键技术。 面对这一飞速演进的技术体系&#xff0c;如何系…

智能体赋能效率,企业知识库沉淀价值:UMI企业智脑的双轮驱动!

智能体企业知识库&#xff1a;UMI企业智脑的核心功能与价值 在人工智能技术飞速发展的今天&#xff0c;企业智能化转型已经成为不可逆转的趋势。作为企业级AI智能体开发平台的佼佼者&#xff0c;优秘智能推出的UMI企业智脑&#xff0c;以其强大的智能体开发能力和全面的企业知…

vue项目 build时@vue-office/docx报错

我在打包vue项目时&#xff0c; 开始用的npm run build和cnpm run build&#xff0c;总是提示 vue-office/docx 错误&#xff0c;尝试过用cnpm重新安装node_modules几次都没用。类似下面的提示一直有。 Error: [commonjs--resolver] Failed to resolve entry for package "…

#RabbitMQ# 消息队列入门

目录 一 MQ技术选型 1 运行rabbitmq 2 基本介绍 3 快速入门 1 交换机负责路由消息给队列 2 数据隔离 二 Java客户端 1 快速入门 2 WorkQueue 3 FanOut交换机 4 Direct交换机 5 Topic交换机 *6 声明队列交换机 1 在配置类当中声明 2 使用注解的方式指定 7 消息转…

EPT(Efficient Prompt Tuning)方法,旨在解决提示调优(Prompt Tuning)中效率与准确性平衡和跨任务一致性的问题

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

云原生安全核心:云安全责任共担模型(Shared Responsibility Model)详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 1. 基础概念 什么是云安全责任共担模型&#xff1f; 云安全责任共担模型&#xff08;Shared Responsibility Model, SRM&#xff09;是云服务提供商&…

go并发与锁之sync.Mutex入门

sync.Mutex 原理&#xff1a;一个共享的变量&#xff0c;哪个线程握到了&#xff0c;哪个线程可以执行代码 功能&#xff1a;一个性能不错的悲观锁&#xff0c;使用方式和Java的ReentrantLock很像&#xff0c;就是手动Lock&#xff0c;手动UnLock。 使用例子&#xff1a; v…

[Java恶补day8] 3. 无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…

LabVIEW教学用开发平台

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

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 知识点回顾&#xff1a; Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09;Dataloader类minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 在遇到大规模数据集时&#xff0c…

vLLM 核心技术 PagedAttention 原理详解

本文是 vLLM 系列文章的第二篇&#xff0c;介绍 vLLM 核心技术 PagedAttention 的设计理念与实现机制。 vLLM PagedAttention 论文精读视频可以在这里观看&#xff1a;https://www.bilibili.com/video/BV1GWjjzfE1b 往期文章&#xff1a; vLLM 快速部署指南 1 引言&#xf…

《软件工程》第 2 章 -UML 与 RUP 统一过程

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

(转)Docker与K8S的区别

1 定义角度 Docker是一种开放源码的应用容器引擎&#xff0c;允许开发人员将其应用和依赖包打包成可移植的容器/镜像中&#xff1b;然后&#xff0c;发布到任何流行的 Linux 或 Windows 机器上&#xff0c;也能实现虚拟化。该容器完全使用沙箱机制&#xff0c;彼此之间没有任何…

商用密码 vs 普通密码:安全加密的核心区别

商用密码 vs 普通密码&#xff1a;安全加密的核心区别 一. 引言&#xff1a;密码的世界二. 什么是普通密码&#xff1f;三. 什么是商用密码&#xff1f;四. 普通密码 vs 商用密码&#xff1a;核心区别五. 选择合适的密码方案六. 结语 前言 肝文不易&#xff0c;点个免费的赞和…

MYSQL中的分库分表及产生的分布式问题

分库分表是分布式数据库架构中常用的优化手段&#xff0c;用于解决单库单表数据量过大、性能瓶颈等问题。其核心思想是将数据分散到多个数据库&#xff08;分库&#xff09;或多个表&#xff08;分表&#xff09;中&#xff0c;以提升系统的吞吐量、查询性能和可扩展性。 一&am…

投影机三色光源和单色光源实拍对比:一场视觉体验的终极较量

一、光源技术&#xff1a;从 “单色模拟” 到 “三色原生” 的进化 &#xff08;一&#xff09;单色光源&#xff1a;白光的 “色彩魔术” 单色光源投影机采用单一白光作为基础光源&#xff0c;通过LCD上出现色彩呈现颜色。这种技术路线的优势在于成本可控&#xff0c;早期被广…

电子电气架构 --- 下一代汽车电子电气架构中的连接性

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…