当前位置: 首页 > article >正文

Linux 线程:从零构建多线程应用:系统化解析线程API与底层设计逻辑

线程

线程的概述

在之前,我们常把进程定义为 程序执行的实例,实际不然,进程实际上只是维护应用程序的各种资源,并不执行什么。真正执行具体任务的是线程。

那为什么之前直接执行a.out的时候,没有这种感受呢?

那是因为每一个进程中都会有一个主线程,我们默认执行的就是这个主线程。

线程创建比进程简单

进程通过返回值确定 是哪块进程的代码。

线程不需要,创建一个线程,比较简单,像回调函数一样,调用线程创建函数,在对应函数体中 操作这一线程即可。

从这往下的概述部分 重点(理解背诵)

进程是系统分配资源的基本单位,线程是CPU执行基本调度的基本单位

比如 如果线程是具体某个人,那么进程就是指部门

线程可以看作一个轻量级的进程(LWP:light weight process),在Linux环境下线程的本质仍是进程

进程 必须至少包含一个线程

线程依赖于进程,线程共享进程的资源,线程的系统资源有(计数器,一组寄存器和栈)

进程结束 当前进程的所有线程 都将立即结束

Linux内核是不区分进程和线程的,只有在用户层面上进行区分。所以,进程所有操作函数pthread*是库函数,而并非系统调用

线程共享资源

  1. 文件描述符
  2. 每种信号的处理方式
  3. 当前工作目录
  4. 用户ID和组ID 内存地址空间

线程非共享资源

  1. 线程id
  2. 处理器现场和栈指针
  3. 独立的栈空间
  4. errno变量
  5. 信号屏蔽字
  6. 调度优先级

线程被CPU调度,因此线程中有调度优先级,且线程间不共享

查看指定进程的线程号的命令:ps -Lf pid(进程号)

线程的API

API介绍用的代码 较简短的代码我用图片展示。

只要看到了pthread.h 头文件,我们在编译的时候就需要加上 -lpthread

pthread_t 是无符号长整型

1、查看线程号

#include <pthread.h>

pthread_t pthread_self(void);

功能:

        查看线程号

参数:

        无参

返回值:

        调用该函数的线程 的 线程ID

代码演示

代码运行结果

线程ID(通过pthread_self得到) 和 IPW(轻量级进程)的区别

大家看这张图,可以看到这两个值有明显的区别

在Linux中,线程就是LWP(轻量级进程),全局唯一,由操作系统内核分配,用于系统调度和资源管理。

线程ID呢仅在同一进程内有效,是抽象标识符。由 pthread 库在进程内维护。

2、创建线程

#include <pthread.h>

int pthread_create(pthread_t *thread,

                                const pthread_attr_t *attr,

                                void *(*start_routine) (void *),

                                void *arg);

功能:

        创建一个线程

参数:

        thread:线程标识符地址

        attr:线程属性结构体地址,通常设置为NULL

                属性这个参数,我们现在填写NULL,下面我会详细说一下这个参数。

        start_routine:线程函数的入口地址

        arg:传递给线程函数的参数

返回值:

        成功:0

        失败:非0

代码演示 案例1

注意这里主进程一定要阻塞,因为进程结束,线程也会关闭

代码运行结果

案例二 创建进程,每个线程有自己的线程函数

代码运行结果是一样的,大家只要知道能够这样用就可以了。

3、回收线程函数

函数介绍

功能:

        等待线程结束(此函数会阻塞),并回收线程资源。如果线程已结束,那么该函数会立即返回。

参数:

        thread:被等待的进程的进程ID

        retval:用来存储线程退出状态的指针的地址

        这里细说一下:retval的返回值类型我们可以看到是void **,这个变量需要用户创建,用来存储创建函数 线程执行函数的 返回值,返回值时void*类型。由于我们要得到它,就要提前创建一个void *的变量,再通过函数修改我们创建的变量为返回值的内容,由于是函数内部要函数外部的变量的值,因此需要传递所创建void *的变量的地址,因此时void **类型。

返回值:

        成功:0

        失败:非0

代码演示

代码运行结果

注意

由于带阻塞,因此有顺序,如下面这种情况

先等待tid1结束,回收tid1后,才会回收tid2

不管谁先结束,都是先1 后2

进程分离

创建好线程后,当多个任务同时进行,用上面的方法,会阻塞线程的释放,导致资源浪费(长时间不适用却霸占内存),因此这里 我们就将其分离出去,把释放工作交给系统,系统发现它结束,就会释放

由于它的归属权已经归于系统,此时我们就不可以再对它使用join

注意这里的分离,并不是该线程不依赖于进程,而是将 释放线程独立资源 的权限交给了系统,进程还是依赖与进程的,依旧共享进程的空间

函数介绍

#include <pthread.h>

int pthread_detach(pthread_t thread);

功能:

        使调用线程的独立资源回收工作与当前进程分离

参数:

        thread:线程ID

返回值:

        成功:0

        失败:非零

代码演示 主线程和子线程

本代码将实现 主线程和子线程 一起运行,并且利用主线程的正常工作,来验证pthread_detach的不阻塞的特性

代码运行结果

4、线程的取消和退出

        注意要退出线程 一定不要调用exit或者_exit 这两个是退出进程的函数,如果调用这个在线程中知道你的进程是什么,它会将进程退出,进程退出会导致所有的线程退出,那么我们该怎么让单个线程退出呢?

1、线程的退出(自杀)

#include <pthread.h>

void pthread_exit(void *retval);

函数功能:

        退出调用线程。一个进程中的多个线程是共享该进程的数据段的,因此通常线程退出后,所占用的资源并不会释放。

参数:

        retval:存储线程退出状态的指针(return后的数据)

返回值:

        无

2、线程的取消(他杀)

取消本线程,也可以取消当前进程的其他线程

#include <pthread.h>

int pthread_cancel(pthread_t thread);

功能:

        退出调用线程。一个进程中的多个线程是共享该进程的数据段的,因此通常线程退出后,所占用的资源并不会释放。

参数:

        thread:目标线程ID

返回值:

        成功:0

        失败:出错编号

注意

        杀死线程也不是立刻就能完成,必须要到达取消点

        取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用

代码演示:

代码功能:子线程1实现5s后自杀,子线程2在7s时杀死子线程3,子线程2在10s时杀死自己。

这里我们会与遇到一个问题:当我们在线程2 中,我们首先需要传入本线程的名字(线程2),还需要传入子线程3的线程ID,我们该如何实现传两个参数呢?

答案在代码中,大家自己查看。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>//传递两个参数的办法就是借助结构体,将线程名和ID作为结构体成员后,将结构体传入 线程调用函数 中即可
//并且如果要实现tid3的修改同步到结构体内,需要传递tid3的指针类型
typedef struct dataDouble
{char name[32];pthread_t *id;
}DATA;//线程调用函数声明
void *my_fun1(void *arg);
void *my_fun2(void *arg);
void *my_fun3(void *arg);
int main(int argc, char const *argv[])
{//创建线程ID遍历(存放线程ID)pthread_t tid1,tid2,tid3;DATA *tid2_data = (DATA *)calloc(1,sizeof(DATA));tid2_data->id = &tid3;strcpy(tid2_data->name,"子进程2");//创建线程pthread_create(&tid1,NULL,my_fun1,(void *)"子线程1");pthread_create(&tid2,NULL,my_fun2,(void *)tid2_data);pthread_create(&tid3,NULL,my_fun3,(void *)"子线程3");//释放线程pthread_detach(tid1);pthread_detach(tid2);pthread_detach(tid3);//阻塞进程while(1);//释放结构体申请空间,一定要在全部线程结束之后free(tid2_data);return 0;
}
//线程调用函数体实现
void *my_fun1(void *arg)//线程1 在5s的时候自杀
{int i = 0;while(1){sleep(1);printf("----%s的运行时间为:%d\n",(char *)arg,++i);if(i == 5){pthread_exit(NULL);}}
}
void *my_fun2(void *arg)//线程2 在7s的时候杀死线程3,在10s的时候自杀(使用cancel)
{DATA data = *(DATA *)arg;int i = 0;while(1){sleep(1);printf("--------%s的运行时间为:%d\n",data.name,++i);if(i == 7){pthread_cancel(*data.id);}if(i == 10){pthread_cancel(pthread_self());}}
}
void *my_fun3(void *arg)
{int i = 0;while(1){sleep(1);printf("------------%s的运行时间为:%d\n",(char *)arg,++i);}
}

代码运行结果

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏夹关注,谢谢大家!!!

下篇介绍:线程的属性介绍,线程池的简述,多线程的建立

相关文章:

Linux 线程:从零构建多线程应用:系统化解析线程API与底层设计逻辑

线程 线程的概述 在之前&#xff0c;我们常把进程定义为 程序执行的实例&#xff0c;实际不然&#xff0c;进程实际上只是维护应用程序的各种资源&#xff0c;并不执行什么。真正执行具体任务的是线程。 那为什么之前直接执行a.out的时候&#xff0c;没有这种感受呢&#xf…...

VMware虚拟机Ubuntu磁盘扩容

VMware中操作&#xff1a; 选择要扩容的虚拟机&#xff0c;点击编辑虚拟机设置 打开后点击磁盘——>点击扩展&#xff08;注意&#xff1a;如果想要扩容的话需要删除快照&#xff09; 调整到你想要的容量 点击上图的扩展——>确定 然后我们进到虚拟机里面 首先&#…...

游戏引擎学习第217天

运行游戏并在 FreeVariableGroup 中遇到我们的断言 其实在美国&#xff0c;某些特定的小糖果&#xff08;例如小糖蛋&#xff09;只在圣诞节和复活节期间出售&#xff0c;导致有些人像我一样在这段时间吃得过多&#xff0c;进而增加体重。虽然这种情况每年都会发生&#xff0c…...

Day 8 上篇:深入理解 Linux 驱动模型中的平台驱动与总线驱动

B站相应的视屏教程&#xff1a; &#x1f4cc; 内核&#xff1a;博文视频 - 总线驱动模型实战全解析 —— 以 PCA9450 PMIC 为例 敬请关注&#xff0c;记得标为原始粉丝。 在 Linux 内核驱动模型中&#xff0c;设备与驱动的组织方式不是随意堆砌&#xff0c;而是基于清晰的分类…...

freertos内存管理简要概述

概述 内存管理的重要性 在嵌入式系统中&#xff0c;内存资源通常是有限的。合理的内存管理可以确保系统高效、稳定地运行&#xff0c;避免因内存泄漏、碎片化等问题导致系统崩溃或性能下降。FreeRTOS 的内存管理机制有助于开发者灵活地分配和释放内存&#xff0c;提高内存利用…...

Dify问题记录 (一)

问题背景 Dify智能体将含有中文的JSON参数传递到Java后端时出现乱码。 解决办法 在HTTP节点前添加代码执行节点&#xff0c;将参数强制编码为UTF-8格式。在Java后端代码中进行解码操作&#xff0c;以确保参数的正确性。 代码如下: 代码执行节点中代码 function main({arg…...

全新突破 | 更全面 · 更安全 · 更灵活

xFile 高可用存储网关 2.0 重磅推出&#xff0c;新增多空间隔离功能从根源上防止数据冲突&#xff0c;保障各业务数据的安全性与独立性。同时支持 NFS、CIFS、FTP 等多种主流文件协议&#xff0c;无需繁琐的数据拷贝转换&#xff0c;即可与现有系统无缝对接&#xff0c;降低集成…...

使用Python建立双缝干涉模型

引言 双缝干涉实验是物理学中经典的实验之一,它展示了光的波动性以及量子力学的奇异性。实验结果表明,当光或粒子通过两条狭缝时,它们会产生干涉现象,形成明暗相间的条纹图案。这种现象不仅说明了光的波动性,还揭示了量子力学的核心思想——粒子具有波动性。今天,我们将…...

T-Box车载系统介绍及其应用

定义 T-Box汽车系统&#xff0c;全称为Telematics - BOX&#xff0c;也常简称为车载T - BOX&#xff0c;是汽车智能系统及车联网系统中的核心组成部分&#xff0c;是安装在车辆上的一种高科技远程信息处理器。 工作原理 T-Box的核心功能主要通过MPU和MCU实现。MPU负责应用程序功…...

SQLyog使用教程

准备工作 链接本地数据库 准备 1&#xff1a;安装mySQL数据库 2&#xff1a;安装SQLyong 连接本地数据库 打开SQLyong应用&#xff0c;将会出现下面的页面 点击新建&#xff0c;输入链接名 输入密码&#xff0c;点击 连接 按钮 如果出现连接错误&#xff0c;且错误号为2058…...

for循环的优化方式、循环的种类、使用及平替方案。

本篇文章主要围绕for循环,来讲解循环处理数据中常见的六种方式及其特点,性能。通过本篇文章你可以快速了解循环的概念,以及循环在实际使用过程中的调优方案。 作者:任聪聪 日期:2025年4月11日 一、循环的种类 1.1 默认有以下类型 原始 for 循环 for(i = 0;i<10;i++){…...

使用 Python 扫描 Windows 下的 Wi-Fi 网络实例演示

使用 Python 扫描 Windows 下的 Wi-Fi 网络 代码实现代码解析 1. 导入库2. 解码混合编码3. 扫描 Wi-Fi 网络4. 运行函数 这是我当前电脑的 wifi 连接界面。 这个是运行的效果图&#xff1a; 代码实现 我们使用了 Python 的 subprocess 模块来调用 Windows 的内置命令 netsh…...

python manimgl数学动画演示_微积分_线性代数原理_ubuntu安装问题[已解决]

1.背景 最近调研python opencv, cuda加速矩阵/向量运算, 对于矩阵的线性变换, 秩, 转秩, 行列式变化等概概念模糊不清. 大概课本依旧是天书, 于是上B站搜索线性代数, 看到 3Blue1Brown 线性变换本质 视频, 点击观看. 惊为天人 --> 豁然开朗 --> 突然顿悟 --> 开心不已…...

【vue3】@click函数传动态变量参数

根据java的学习&#xff0c;摸索了一下vue3 函数传参的方式。以此作为记录。有更好的其它方式&#xff0c;可以评论区补充。 <script> const tmpref(); </script><button click"tmpFunction(传递参数:tmp)">按钮</button> // 直接【字符串…...

用matplotlib生成一个炫酷的爱心

下面是结合数学方程和可视化技巧&#xff0c;生成一个炫酷的爱心效果&#xff1a; import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation # 创建画布 fig plt.figure(figsize(8, 8)) ax plt.axes(xlim(-2.5, 2.5), ylim(-3,…...

【leetcode hot 100 300】最长递增子序列

错误解法&#xff1a;在每次更新db[i]时&#xff0c;如果当前nums[i]>nums[i-1]就db[i-1]1&#xff0c;否则db[i-1] class Solution {public int lengthOfLIS(int[] nums) {int n nums.length;int[] db new int[n]; // db[i]表示到i的最长严格递增子序列的长度db[0] 1;f…...

oracle 12c密码长度,复杂度查看与设置

一 密码长度和复杂度 Oracle 数据库通过 PASSWORD_VERIFY_FUNCTION 来控制密码复杂度。 1.1 查看当前的密码复杂度设置 SELECT * FROM dba_profiles WHERE resource_name PASSWORD_VERIFY_FUNCTION; LIMIT表示分配给该 PROFILE 的密码验证函数名称。如果为 NULL&#xff0c;…...

数据结构——哈希技术及链地址法

目录 一、哈希的定义 二、哈希冲突定义 三、构造哈希函数的方法 四、四种解决哈希冲突的方法 4.1 开放地址法 4.2 链地址法 4.3 再散列函数法 4.4 公共区溢出法 五、链地址法结构体设计 六、基本操作的实现 6.1 哈希函数 6.2 初始化 6.3 插入值 6.4 删除值 6.5 查…...

开源CMS的模块化设计和API接口如何具体影响其扩展性?

优秀的CMS系统都有自己主打的特点&#xff0c;开源CMS凭借其灵活性和低成本优势占据了市场主流地位&#xff0c;而模块化设计与API接口正是其扩展性的两大基石。本文将深入探讨这两大技术特性是如何影响cms的扩展性的。 一、模块化设计&#xff1a;功能解耦与生态繁荣的引擎 …...

【Docker】快速部署 Certbot 并为 Nginx 服务器配置 SSL/TLS 证书

【Docker】快速部署 Certbot 并为 Nginx 服务器配置 SSL/TLS 证书 引言 Certbot 是一个免费的开源工具&#xff0c;用于自动化管理和获取 SSL/TLS 证书&#xff0c;主要用于与 Let’s Encrypt 证书颁发机构交互。 步骤 Nginx 挂载 certbot 文件夹。 docker run -d \--name…...

Redis下载稳定版本5.0.4

https://www.redis.net.cn/download/ Redis下载 Redis 版本号采用标准惯例:主版本号.副版本号.补丁级别,一个副版本号就标记为一个标准发行版本,例如 1.2,2.0,2.2,2.4,2.6,2.8,奇数的副版本号用来表示非标准版本,例如2.9.x发行版本是Redis 3.0标准版本的非标准发行版本…...

Google Chrome下载受限制的解决方案【方法指南】

在国内使用网络时&#xff0c;部分用户在尝试访问Google Chrome官网下载谷歌浏览器时&#xff0c;常常遇到网页无法打开或文件下载失败的情况。这种下载受限制的问题多由网络访问政策或DNS解析异常导致。为了正常获取Google Chrome的最新版安装程序&#xff0c;用户需要通过一些…...

Linux关于git上传大文件的解决方案:使用Git LFS

最近想要上传sdk到gitlab仓库上&#xff0c;但是使用git push的时候发现限制文件上传的大小限制到了100MB。 保持当前仓库的干净&#xff0c;要么重新拉取&#xff0c;要么git reset HEAD^ --hard这个命令来重新进行commit&#xff0c;直到撤回到代码上显示没有commit的地方为止…...

JAVA后端八股面试经验总结-前言篇

1️⃣个人暑期实习面试情况 暑期实习面试告一段段落了 陆陆续续大小厂有20&#xff0b;Java后端开发的面经 2️⃣为什么要写这个呢&#xff1f; ①首先&#xff0c;了解最重点的面试最爱问的题型有哪些&#xff1f; 我会整理出我面到过至少2次的题目和回答方式&#xff0…...

《计算机名人堂》专栏介绍:先驱之路

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 &#x1f31f;引言&#xff1a;先驱之路一、出发点&#xff1a;讲述数字世界的缔…...

参照Spring Boot后端框架实现序列化工具类

本文参照Jackson实现序列化工具类&#xff0c;旨在于简化开发 JacksonUtil.class public class JacksonUtil {private JacksonUtil() {}/*** 单例*/private final static ObjectMapper OBJECT_MAPPER;static {OBJECT_MAPPER new ObjectMapper();}private static ObjectMappe…...

mysql 删除表等待

今天有个表加字段&#xff0c;语句是先删除&#xff0c;后重新建&#xff0c;表没有数据 &#xff0c;但是删除一个表的时候&#xff0c;语句drop table 提示超时 show processlist 后&#xff0c;等待类型是 Waiting for table metadata lock 取消重试几次后仍然是如此…...

MCP工具的配置文件格式是怎么样的?MCP教程平台推荐

MCP&#xff08;Model Context Protocol&#xff09;配置文件是AI开发中连接MCP服务器的核心文件&#xff0c;采用JSON格式定义服务参数。它广泛应用于Cursor、ChatWise等AI开发工具&#xff0c;帮助开发者快速配置本地或远程MCP服务。本文将深入解析MCP配置文件的结构、获取方…...

网络安全法规与入门指南

在当今数字化时代&#xff0c;网络安全已成为保障个人隐私、企业利益和国家安全的关键领域。随着网络攻击的日益复杂和频繁&#xff0c;了解和遵守网络安全法规变得尤为重要。本文将深入探讨网络安全相关法规&#xff0c;并为想要进入这一领域的读者提供实用的入门指南。 一、…...

医院访客登记如何做才能更高效?

在医院工作过的朋友&#xff0c;大概都有过这样的体验&#xff1a;一到探视时间&#xff0c;门诊大厅、病房入口就开始拥堵&#xff0c;尤其是一些管控较严的科室&#xff0c;如ICU、手术区、儿科病房&#xff0c;来访人员必须逐一登记信息。人一多&#xff0c;就容易出错、漏登…...