Linux 线程详解

目录
一、线程概述
二、线程创建
三、线程终止
四、线程回收
五、线程取消
六、线程分离
七、线程安全
一、线程概述
线程是进程内的一个执行单元,是进程内可调度的实体。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,但拥有各自独立的程序计数器、栈和寄存器等执行上下文。线程相较于进程,创建和切换开销更小,能更有效地利用多核处理器资源,提高程序的并发性能。
二、线程创建
在 Linux 中,使用 pthread_create 函数来创建线程,其函数原型如下:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg);
thread:指向pthread_t类型的指针,用于存储新创建线程的标识符。attr:指向pthread_attr_t类型的指针,用于设置线程的属性,若为NULL,则使用默认属性。start_routine:是一个函数指针,指向线程开始执行的函数,该函数返回值为void*类型,参数也为void*类型。arg:传递给start_routine函数的参数。
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *thread_function(void *arg) {// 线程函数,这里简单打印线程 ID 和传入的参数int num = *(int *)arg;printf("线程 ID:%lu,参数:%d\n", pthread_self(), num);pthread_exit(NULL);
}int main() {pthread_t thread_id;int num = 10;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, (void *)&num)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 主线程继续执行其他任务,这里简单睡眠一段时间sleep(2);// 等待线程结束(可选,这里只是为了确保线程在主线程退出前完成)pthread_join(thread_id, NULL);printf("主线程结束\n");return 0;
}
在上述代码中,首先定义了一个线程函数 thread_function,它接受一个 void* 类型的参数,并在线程中打印线程 ID 和传入的参数值。在 main 函数中,创建了一个线程,将 num 的地址作为参数传递给线程函数。主线程睡眠一段时间后,可以选择调用 pthread_join 等待子线程结束,确保子线程资源被正确回收后主线程再退出。
三、线程终止
线程可以通过以下几种方式终止:
1.线程函数自然返回:线程函数执行完所有指令后返回,线程自动终止。
2.调用 pthread_exit 函数:线程可以在任何位置调用 pthread_exit 函数来显式终止自身,并可以返回一个指向线程退出状态的指针。
函数原型:void pthread_exit(void *retval);
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后主动终止printf("线程即将终止\n");pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 等待线程结束(这里只是为了演示主线程的行为,实际上对于主动退出的线程,等待并非必需)pthread_join(thread_id, NULL);printf("主线程继续执行\n");return 0;
}
在上述代码中,线程函数 thread_function 执行到 pthread_exit(NULL) 时线程终止,主线程可以通过 pthread_join 来等待该线程结束,以确保线程资源被正确回收。
四、线程回收
pthread_join 函数用于回收已经终止的线程资源,并获取线程的退出状态(如果线程通过 pthread_exit 传递了退出状态)。
函数原型:int pthread_join(pthread_t thread, void **retval);
thread:要等待回收的线程 ID。
retval:用于存储线程的退出状态指针,如果不关心线程的退出状态,可以传入 NULL。
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后返回一个整数值作为退出状态int *result = (int *)malloc(sizeof(int));*result = 42;pthread_exit((void *)result);
}int main() {pthread_t thread_id;void *thread_result;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 回收线程并获取退出状态if (pthread_join(thread_id, &thread_result)!= 0) {perror("线程回收失败");exit(EXIT_FAILURE);}// 输出线程的退出状态printf("线程退出状态:%d\n", *(int *)thread_result);free(thread_result);printf("主线程继续执行\n");return 0;
}
五、线程取消
pthread_cancel 函数用于请求取消一个线程的执行,但线程必须处于可取消状态并且能够响应取消请求(线程默认是可取消的,但有些情况下可能需要修改线程的可取消性状态)。
函数原型:int pthread_cancel(pthread_t thread);
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *thread_function(void *arg) {// 线程执行一个循环,每隔一秒打印一次信息for (int i = 0; i < 10; ++i) {printf("线程运行中...\n");sleep(1);}pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 主线程睡眠 5 秒后尝试取消子线程sleep(5);if (pthread_cancel(thread_id)!= 0) {perror("线程取消失败");exit(EXIT_FAILURE);}// 回收线程(即使线程被取消,也应该进行回收以确保资源释放)pthread_join(thread_id, NULL);printf("主线程继续执行\n");return 0;
}
在上述代码中,主线程创建了一个子线程后,睡眠 5 秒,然后调用 pthread_cancel 尝试取消子线程的执行。子线程在执行过程中如果被成功取消,会立即终止,然后主线程通过 pthread_join 回收子线程资源。
六、线程分离
线程分离是将线程设置为脱离状态,这样线程结束后其资源会自动被系统回收,而不需要其他线程调用 pthread_join 来回收。
函数原型:int pthread_detach(pthread_t thread);
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后自动终止,由于被分离,不需要其他线程回收printf("线程执行任务后将自动释放资源\n");pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 分离线程if (pthread_detach(thread_id)!= 0) {perror("线程分离失败");exit(EXIT_FAILURE);}// 主线程继续执行其他任务,无需等待被分离的线程printf("主线程继续执行,无需等待子线程\n");return 0;
}
在这个例子中,创建线程后立即调用 pthread_detach 将线程设置为分离状态,线程执行完任务后会自动释放资源,主线程无需调用 pthread_join 等待其结束。
七、线程安全
线程安全是指多个线程同时访问共享资源时,不会产生数据竞争和不一致的问题。实现线程安全通常需要使用同步机制,如互斥锁、自旋锁、读写锁、条件变量等。
以下是一个使用互斥锁实现线程安全的示例,多个线程对一个共享变量进行自增操作:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>// 共享变量
int shared_variable = 0;
// 互斥锁
pthread_mutex_t mutex;void *thread_function(void *arg) {for (int i = 0; i < 10000; ++i) {// 加锁,保护共享资源pthread_mutex_lock(&mutex);shared_variable++;// 解锁pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
}int main() {pthread_t thread1, thread2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建两个线程if (pthread_create(&thread1, NULL, thread_function, NULL)!= 0) {perror("线程 1 创建失败");exit(EXIT_FAILURE);}if (pthread_create(&thread2, NULL, thread_function, NULL)!= 0) {perror("线程 2 创建失败");exit(EXIT_FAILURE);}// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("共享变量最终值:%d\n", shared_variable);return 0;
}
在上述代码中,两个线程并发地对 shared_variable 进行自增操作。通过使用互斥锁 mutex,每次只有一个线程能够进入临界区(pthread_mutex_lock 和 pthread_mutex_unlock 之间的代码段)对共享变量进行操作,从而保证了数据的一致性和线程安全。如果没有互斥锁的保护,多个线程同时对共享变量进行读写操作可能会导致数据竞争和错误的结果。
相关文章:
Linux 线程详解
目录 一、线程概述 二、线程创建 三、线程终止 四、线程回收 五、线程取消 六、线程分离 七、线程安全 一、线程概述 线程是进程内的一个执行单元,是进程内可调度的实体。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空…...
云架构:考量与框架
云架构:考量与框架 引言 在当今的数字化环境中,云计算已成为现代商业运营的基石。一个设计良好的云架构框架为可扩展、安全和弹性的系统奠定了基础。本文将深入探讨云架构的核心要素,讨论重要的考量因素、设计指南,以及最佳实践…...
SD下载、安装、使用、卸载-Stable Diffusion整合包v4.10发布!
目录 前言概述 SD安装1、安装软件2、启动3、配置4、运行5、测试 导入SD模型【决定画风】常用模型下载安装模型 SD卸载SD文生图提示词提示词使用技巧提示词的高级使用技巧强调关键词 前言 我向来不喜欢搞一些没有用的概念,所以直接整理可能用到的东西。 sd简单的说…...
java 发送邮件
前期准备 pom文件中引入 JavaMail API 和 JavaBean Activation FrameWork,得到两个jar包:mail.jar 和 activation.jar 发送简单邮件(只有邮件正文,普通文本) package com.zbttest.email;import com.sun.mail.util.Ma…...
聚类系列 (二)——HDBSCAN算法详解
在进行组会汇报的时候,为了引出本研究动机(论文尚未发表,暂不介绍),需要对DBSCAN、OPTICS、和HDBSCAN算法等进行详细介绍。在查询相关资料的时候,发现网络上对于DBSCAN算法的介绍非常多与细致,但…...
AngularJS HTML DOM
关于《AngularJS HTML DOM》的文章,我找到了一些有用的信息。这篇文章主要介绍了AngularJS如何通过特定的指令与HTML DOM元素进行交互。以下是一些关键点: ng-disabled 指令:这个指令用于将应用程序数据绑定到HTML的disabled属性。例如&#…...
C语言延时实现
C语言延时实现 在C语言中,delay 函数通过空循环实现延时,而不是像其他高级语言(如Python)直接使用 sleep 函数。这种实现方式是基于单片机的特性和C语言的底层操作。下面详细解释为什么这种空循环可以实现延时,以及它…...
OSI模型的网络层中产生拥塞的主要原因?
( 1 )缓冲区容量有限;( 1.5 分) ( 2 )传输线路的带宽有限;( 1.5 分) ( 3 )网络结点的处理能力有限;( 1 分…...
机器学习周报-ModernTCN文献阅读
文章目录 摘要Abstract 0 提升有效感受野(ERF)1 相关知识1.1 标准卷积1.2 深度分离卷积(Depthwise Convolution,DWConv)1.3 逐点卷积(Pointwise Convolution,PWConv)1.4 组卷积(Grou…...
什么是网关路由
1.认识网关 网关(Gateway)和路由(Router)是两个相关但不同的概念。 一、网关(Gateway) 定义 网关是一个网络节点,它充当了不同网络之间的连接点。可以将其看作是一个网络的 “大门”…...
信号的产生、处理
一、信号的概念 信号是linux系统提供的一种,向指定进程发送特定事件的方式。收到信号的进程,要对信号做识别和处理。信号的产生是异步的,进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种(用指令kill -l查看&…...
在Linux中,zabbix如何监控脑裂?
在Linux中,zabbix监控脑裂主要涉及对高可用(HA)系统中可能发生的节点间通信中断或不一致状态的监控。脑裂问题通常发生在具有冗余节点的高可用系统中,如集群、HA系统或分布式数据库系统,当节点之间失去通信时ÿ…...
C++基础概念复习
前言 本篇文章作基础复习用,主要是在C学习中遇到的概念总结,后续会继续补充。如有不足,请前辈指出,万分感谢。 1、什么是封装,有何优点,在C中如何体现封装这一特性? 封装是面向对象编程&…...
Earth靶场
打开靶机后使用 arp-scan -l 查询靶机 ip 我们使用 nmap 进行 dns 解析 把这两条解析添加到hosts文件中去,这样我们才可以访问页面 这样网站就可以正常打开 扫描ip时候我们发现443是打开的,扫描第二个dns解析的443端口能扫描出来一个 txt 文件 dirsear…...
JavaScript 日期格式
在 JavaScript 中,日期格式可以通过 Date 对象进行操作和格式化。下面是一些常见的 JavaScript 日期格式及其示例: 1. ISO 8601 格式 ISO 8601 是一种标准的日期和时间表示方法,格式为 YYYY-MM-DDTHH:mm:ss.sssZ,例如: let date = new Date(); console.log(date.toISOS…...
django vue3实现大文件分段续传(断点续传)
前端环境准备及目录结构: npm create vue 并取名为big-file-upload-fontend 通过 npm i 安装以下内容"dependencies": {"axios": "^1.7.9","element-plus": "^2.9.1","js-sha256": "^0.11.0&quo…...
xiaoya小雅超集使用夸克网盘缓存教程
距离上一次小白写到关于小雅的教程已经过去了一周的时间,这段时间里,有很多小伙伴都想知道怎么用夸克网盘作为小雅的缓存。 今天这不就来了吗? 这段时间确实是比较忙,毕竟快过年了嘛,辛辛苦苦一整年,至少…...
计算机基础知识复习1.4
数据库事务 #开启一个事务 start transaction #执行SQL语句 SQL1 SQL2 .. #提交事务 commit 类加载器 启动类加载器:负责加载Java的核心库 用C编写,是JVM的一部分,启动类加载器无法被Java程序直接引用 扩展类加载器:是Java语…...
SpringMVC(三)请求
目录 一、RequestMapping注解 1.RequestMapping的属性 实例 1.在这里创建文件,命名为Test: 2.复现-返回一个页面: 创建test界面(随便写点什么): Test文件中编写: 编辑 运行: 3.不返回…...
Node.js应用程序遇到了内存溢出的问题
vue 项目 跑起来,一直报错,内存溢出 在 文件node_modules 里 .bin > vue-cli-service.cmd 在依赖包这个文件第一行加上这个 node --max-old-space-size102400 "%~dp0\..\vue\cli-service\bin\vue-cli-service.js" %* node --max-old-s…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
