【Linux操作系统】多线程控制(创建,等待,终止、分离)
目录
- 一、线程与轻量级进程的关系
- 二、进程创建
- 1.线程创建
- 线程创建函数(pthread)
- 查看和理解线程id
- 主线程与其他线程之间的关系
- 三、线程等待(回收)
- 四、线程退出
- 线程退出情况
- 线程退出方法
- 五、线程分离
- 线程的优点
- 线程的缺点
一、线程与轻量级进程的关系
首先我想问一个问题,Linux中有没有真线程呢? 答案是没有,Linux中只有轻量级进程
但是用户不知道“轻量级进程”,认为只有进程和线程。因此,Linux系统中不会有与线程相关的系统调用,只有轻量级进程的系统调用
Linux系统为了能让用户进行正常的使用Linux线程,Linux设计者在用户与内核之间设计了一个 pthread库—原生线程库
**作用是将轻量级进程的系统调用进行封装,转成线程相关的接口语义提供给用户,让用户感觉自己使用的是线程,但实际上底层是轻量级进程 **,所以我们平时在Linux中使用线程必须带上这个库, 不过要注意的是这个库并不属于LInux内核,只要是库它就是在用户级实现的,因此有许多人将Linux的线程称作“用户级线程”,所有线程的实现都是在 用户级实现的
二、进程创建
1.线程创建
线程创建函数(pthread)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
- thread:返回线程ID
- attr:设置线程的属性,attr为NULL表示使用默认属性
- start_routine:函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数
- 返回值:成功放回0,失败返回错误码
举例:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>void *newthreadrun(void *args)
{while (true){std::cout << "I am new thread, pid: " << getpid() << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, newthreadrun, nullptr);while (true){std::cout << "I am main thread, pid: " << getpid() << std::endl;sleep(1);}
}
有个注意事项就是,如果我们创建线程传递的是 类成员函数 ,那么就需要将我们的类成员函数声明为static函数,因为如果类成员函数的参数中默认会有一个this指针,将类成员函数声明为static函数,这个成员函数就没有this指针了,否则就会造成匹配错误
查看和理解线程id
使用 ps -aL
查看
当我们使用 ps -aL
查看进程时我们会发现除了我们熟知的PID,还有一个陌生的LWP
如果这个进程是单进程的话,这个PID和LWP是一样的
如果这个进程是多线程的话,这个LPW代表不同线程(轻量级进程(light weight process))LWP
其中这个PID是主线程的ID
pthread_ create
函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事,前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程
pthread_ create
函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的
线程库NPTL提供了pthread_ self
函数,可以获得线程自身的ID:
pthread_t pthread_self(void);
主线程与其他线程之间的关系
- 主线程与其他线程谁先运行?
这个答案是不确定的,由调度器决定
- 主线程退出,其他线程继续运行还是退出?
主线程退出,就等同于这个进程退出了,而进程是承担分配系统资源的基本实体,进程退出了,进程所拥有的内部资源也应该被释放,也包括内部的执行流等,所以只要主线程退出了,所有进程都要退出
- 这也意味着我们的主线程需要最后结束
三、线程等待(回收)
前面我们学过,进程在退出时需要回收它(wait),否则就会变成僵尸进程,而线程同样如此,线程也需要被回收(wait),否则也会造成内存泄漏问题
线程等待函数 :pthread_join
int pthread_join(pthread_t thread, void **value_ptr);
- thread:线程id
- value_ptr:它指向一个指针,后者指向线程的返回值
- 返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待,直到id为thread的线程终止。
thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的:
- 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值
- 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED。
- 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数
四、线程退出
线程退出情况
线程退出无非三种情况:
- 代码跑完,结果对
- 代码跑完,结构错
- 代码出异常
重点是出异常情况:
如果在多线程中,任意一个进程出现异常,都会导致整个进程退出,因此有些人说多线程代码往往健壮性不好
线程退出方法
- 使用return
从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 使用pthread_exit函数
线程可以调用pthread_ exit终止自己,类似于进程退出的exit
void pthread_exit(void *value_ptr);
无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了
- 使用pthread_cancel函数
取消一个执行中的线程
int pthread_cancel(pthread_t thread);
- thread:线程ID
- 返回值:成功返回0;失败返回错误码
五、线程分离
默认情况下,新创建的线程是joinable的,也就是说,在线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏,导致类似僵尸进程的事件
但如果我们的主线程并不关心其他线程的返回值,join它是一种负担,这个时候,我们可以告诉系统,当该线程退出时,自动释放该线程资源,也就是说,我们可以将这个线程分离出去,可以说使该线程与主线程达到真正的并行
不过所谓的分离只是线程的一种工作状态,并不是说页表、进程地址空间等分离了,底层依旧属于同一个进程,因此当主线程退出时,分离线程还是会跟着退出,只是不需要等待了
线程并行方法:
- 在主线程分离其他线程
int pthread_detach(pthread_t thread);
- 在该线程中分离线程
pthread_detach(pthread_self());
举例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* thread_run(void* arg)
{pthread_detach(pthread_self());printf("%s\n", (char*)arg);return NULL;
}
int main(void)
{pthread_t tid;if (pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0) {printf("create thread error\n");return 1;}int ret = 0;sleep(1);if (pthread_join(tid, NULL) == 0) {printf("pthread wait success\n");ret = 0;}else {printf("pthread wait failed\n");ret = 1;}return ret;
}
线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
线程的缺点
- 性能损失
一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型
线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的 同步和调度开销,而可用的资源不变。
- 健壮性降低
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了
不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
- 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
- 编程难度提高
编写与调试一个多线程程序比单线程程序困难得多
相关文章:

【Linux操作系统】多线程控制(创建,等待,终止、分离)
目录 一、线程与轻量级进程的关系二、进程创建1.线程创建线程创建函数(pthread)查看和理解线程id主线程与其他线程之间的关系 三、线程等待(回收)四、线程退出线程退出情况线程退出方法 五、线程分离线程的优点线程的缺点 一、线程…...

二百七十八、ClickHouse——将本月第一天所在的那一周视为第一周,无论它是从周几开始的,查询某个日期是本月第几周
一、目的 ClickHouse指标表中有个字段week_of_month,含义是这条数据属于本月第几周。 而且将本月第一天所在的那一周视为第一周,无论它是从周几开始的。比如2024-12-01是周日,即12月第一周。而2024-12-02是周一,即12月第二周 二…...
JVM八股文精简
目录 简述JVM类加载过程简述JVM中的类加载器简述双亲委派机制双亲委派机制的优点简述JVM内存模型简述程序计数器简述虚拟机栈简述本地方法栈简述JVM中的堆简述方法区简述运行时常量池简述Java创建对象的过程简述JVM给对象分配内存的策略Java对象内存分配是如何保证线程安全的如…...
深入解析CMake中的find_package()命令:工作原理及实际应用示例
深入解析CMake中的find_package()命令:工作原理及实际应用示例 在CMake中,find_package() 是一个复杂而强大的命令,用于在构建系统中定位外部依赖(通常是库),并配置必要的编译和链接设置。这个命令允许开发…...

使用数据层进行数据生命周期管理
作者:来自 Elastic Stef Nestor Elasticsearch 7.10 使配置数据生命周期变得不再那么复杂。在这篇博文中,我将介绍一些变化、如何使用它们以及一些最佳实践。 数据生命周期可以包含很多阶段,因此我们将涉及: 将集群划分为层&…...

Kubernetes架构原则和对象设计
云原生学习路线导航页(持续更新中) 快捷链接 Kubernetes常见问题解答 本文从 Google Borg系统的架构设计开始,深入讲解Kubernetes架构及组件的基本原理 1.什么是云计算 1.1.传统行业应用 假设有10台服务器,两个应用。小规模管…...

响应式编程一、Reactor核心
目录 一、前置知识1、Lambda表达式2、函数式接口 Function3、StreamAPI4、Reactive-Stream1)几个实际的问题2)Reactive-Stream是什么?3)核心接口4)处理器 Processor5)总结 二、Reactor核心1、Reactor1&…...
uniapp+vue3+ts请求接口封装
1.安装luch-request yarn add luch-requestnpm install luch-request2.新建文件src/utils/request.ts 需要自己修改config.baseURL和token(获取存储的token) // import HttpRequest from luch-request; import type { HttpRequestConfig, HttpRespons…...

【计算机网络】实验4:生成树协议STP的功能以及虚拟局域网VLAN
实验 4:生成树协议STP的功能以及虚拟局域网VLAN 一、 实验目的 加深对生成树协议STP的功能的理解。 了解虚拟局域网VLAN。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、验证交换机生成树协议STP的功能 (1) 第一步:构建网络拓…...

基于Matlab BP神经网络的电力负荷预测模型研究与实现
随着电力系统的复杂性和规模的不断增长,准确的电力负荷预测对于电网的稳定性和运行效率至关重要。传统的负荷预测方法依赖于历史数据和简单的统计模型,但这些方法在处理非线性和动态变化的负荷数据时,表现出较大的局限性。近年来,…...

java 21 多线程
1.相关概念 进程: 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷配新的内存空间。 进程是程序的一次执行过程&#…...
Rust学习笔记_07——枚举和范围
Rust学习笔记_04——引用 Rust学习笔记_05——控制流(1) Rust学习笔记_06——控制流(2) 文章目录 1. 枚举1.1基础1.2 给枚举变体起个“别名”1.3 枚举与匹配(match) 2. 范围2.1 介绍2.2 半开区间范围2.3 包含范围的语法糖2.4 步长范围(Range …...

40分钟学 Go 语言高并发:服务性能调优实战
服务性能调优实战 一、性能优化实战概述 优化阶段主要内容关键指标重要程度瓶颈定位收集性能指标,确定瓶颈位置CPU、内存、延迟、吞吐量⭐⭐⭐⭐⭐代码优化优化算法、并发、内存使用代码执行时间、内存分配⭐⭐⭐⭐⭐系统调优调整系统参数、资源配置系统资源利用率…...
Windows通过指令查看已安装的驱动
Windows通过指令查看已安装的驱动 在 Windows 操作系统中,有几种命令可以用来查看已安装的驱动程序。以下是常见的几种方法: 1. 使用 pnputil 查看已安装驱动程序 pnputil 是一个 Windows 内置工具,可以列出所有已安装的驱动程序包。 命令…...

Windows 11 如何配置node.js
一,官网下载 官网首页 下载最新LTS版本,比较稳定,如果想探索更新的版本去探索新的nodejs功能。 1. 下载完成后,双击运行程序,点击next 2. 勾选接受协议,点击next 3. 选择自己的安装路径(默认是…...
AWTK fscript 中的 串口 扩展函数
fscript 是 AWTK 内置的脚本引擎,开发者可以在 UI XML 文件中直接嵌入 fscript 脚本,提高开发效率。本文介绍一下 fscript 中的 ** 串口 扩展函数 ** 1.iostream_serial_create 创建串口输入输出流对象。 原型 iostream_serial_create(device) > ob…...
yolov11剪枝
思路:yolov11中的C3k2与yolov8的c2f的不同,所以与之前yolov8剪枝有稍许不同; 后续:会将剪枝流程写全,以及增加蒸馏、注意力、改loss; 注意: 1.在代码105行修改pruning.get_threshold(yolo.mo…...

智慧地图聚合(LockMap)标注系统开发说明文档
智慧地图聚合(LockMap)标注系统开发说明文档 1. 系统概述 智慧地图聚合(LockMap)标注系统是一个专为处理大规模地理信息数据而设计的综合解决方案。通过后端高效的数据管理和前端直观的地图展示,该系统能够实现对海量地理位置点的有效可视化。本项目旨在提供一个用…...

「Mac畅玩鸿蒙与硬件36」UI互动应用篇13 - 数字滚动抽奖器
本篇将带你实现一个简单的数字滚动抽奖器。用户点击按钮后,屏幕上的数字会以滚动动画的形式随机变动,最终显示一个抽奖数字。这个项目展示了如何结合定时器、状态管理和动画实现一个有趣的互动应用。 关键词 UI互动应用数字滚动动画效果状态管理用户交…...

cuda12.1版本的pytorch环境安装记录,并添加到jupyter和pycharm中
文章目录 前置准备使用anaconda prompt创建虚拟环境创建虚拟环境激活pytorch虚拟环境把pytorch下载到本地使用pip把安装包安装到pytorch环境中进入python环境检验是否安装成功将环境添加到jupyter在pycharm中使用该环境: 前置准备 安装anaconda,我的版本…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...