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);}
}

代码运行结果

结束

代码重在练习!

代码重在练习!

代码重在练习!

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

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

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

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

相关文章

014_多线程

多线程 多线程创建线程方式一&#xff1a;继承Thread类方式二&#xff1a;实现Runable接口方式三&#xff1a;实现Callbale接口 Thread的常用方法线程安全线程同步方式一&#xff1a;同步代码块同步方法方式三&#xff1a;Lock锁 线性池创建线程池处理Runnable任务处理Callable…

机场跑道异物检测数据集VOC+YOLO格式33793张31类别

数据集分辨率都是300x300,都是贴近地面拍摄&#xff0c;具体看图片 据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;33793 标注数量(xml文件…

Spring Cloud 远程调用

4.OpenFeign的实现原理是什么&#xff1f; 在使用OpenFeign的时候&#xff0c;主要关心两个注解&#xff0c;EnableFeignClients和FeignClient。整体的流程分为以下几个部分&#xff1a; 启用Feign代理&#xff0c;通过在启动类上添加EnableFeignClients注解&#xff0c;开启F…

Unity中使用FMETP STREAM传输实时画面

一、客户端&#xff08;发送端&#xff09; 总体思路&#xff1a;先把画面编码Encoder&#xff0c;再发送给服务端 新建场景&#xff0c;创建一个实体&#xff0c;名为FMnet&#xff0c;添加组件FMNetworkManager&#xff0c;将NetworkType设置为客户端Client&#xff0c;设置…

Baklib三步构建企业内容中台

需求调研构建内容中台 企业内容中台建设的首要环节在于精准识别业务需求与知识管理痛点。通过Baklib 是什么类型的工具的定位分析可知&#xff0c;其作为知识管理中枢&#xff0c;能够系统梳理客户服务场景中的高频咨询、产品文档更新需求及跨部门协作流程。在需求调研阶段&am…

实现抗隐私泄漏的AI人工智能推理

目录 什么是私人AI? 什么是可信执行环境? TEE 如何在 AI 推理期间保护数据? 使用 TEE 是否存在风险? 有哪些风险? Atoma 如何应对这些风险 为什么去中心化网络是解决方案 人工智能推理过程中还有其他保护隐私的方法吗? 私人人工智能可以实现什么? 隐私驱动的应…

一、TorchRec里边的输入输出类型

TorchRec中的输入和输出格式 文章目录 TorchRec中的输入和输出格式前言一、JaggedTensor1.1 核心概念1.2 核心属性&#xff0c;也就是参数1.3 关键操作与方法 二、KeyedJaggedTensor2.1 核心概念2.2 核心属性&#xff0c;也就是参数 3、KeyedTensor总结 前言 TorchRec具有其特…

JAVA实现在H5页面中点击链接直接进入微信小程序

在普通的Html5页面中如何实现点击URL链接直接进入微信小程序&#xff0c;不需要扫描小程序二维码&#xff1f; 网上介绍的很多方法是在小程序后台设置Schema&#xff0c;不过我进入我的小程序后台在开发设置里面 没有找到设置小程序Schema的地方&#xff0c;我是通过调用API接口…

uniapp解决上架华为应用市场审核要求-监听权限的申请

支持android平台全局监听权限的申请。当申请权限时&#xff0c;会在页面顶部显示申请权限的目的。主要解决上架华为应用市场审核要求&#xff1a;APP在调用终端权限时&#xff0c;应同步告知用户申请该权限的目的。 因为如果不提示&#xff0c;你上架应用市场会被打打回来 Tip…

文件IO5(JPEG图像原理与应用)

JPEG图像原理与应用 ⦁ 基本概念 JPEG&#xff08;Joint Photographic Experts Group&#xff09;指的是联合图像专家组&#xff0c;是国际标准化组织ISO制订并于1992年发布的一种面向连续色调静止图像的压缩编码标准&#xff0c;所以也被称为JPEG标准。 同样&#xff0c;JP…

vue3 history路由模式刷新页面报错问题解决

在使用history路由模式时刷新网页提示404错误&#xff0c;这是改怎么办呢。 官方解决办法 https://router.vuejs.org/zh/guide/essentials/history-mode.html

3D激光轮廓仪知识整理(待完善)

文章目录 1.原理和应用场景1.1 相机原理1.1.1 测量原理1.1.2 相机激光器1.1.3 沙姆镜头1.1.4 相机标定1.1.5 中心线提取 1.2 应用场景1.2.1 测量相关应用1.2.2 缺陷检测相关应用 2.相机参数介绍及选型介绍2.1 成像原理2.2 原始图成像2.3 生成轮廓图2.4 相机规格参数2.4.1 单轮廓…

w285药店管理系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

Google Chrome Canary版官方下载及安装教程【适用于开发者与进阶用户】

谷歌浏览器&#xff08;Google Chrome&#xff09;以其高性能、强扩展性和良好的用户体验深受全球用户喜爱。在其多个版本中&#xff0c;Chrome Canary因具备最前沿的功能测试环境&#xff0c;成为开发者和技术探索者的首选。如果你希望第一时间体验Google Chrome最新功能&…

RocketMQ深度百科全书式解析

​一、核心架构与设计哲学​ ​1. 设计目标​ ​海量消息堆积​&#xff1a;单机支持百万级消息堆积&#xff0c;适合大数据场景&#xff08;如日志采集&#xff09;。​严格顺序性​&#xff1a;通过队列分区&#xff08;Queue&#xff09;和消费锁机制保证局部顺序。​事务…

每日一题(小白)暴力娱乐篇19

样例&#xff1a; 6 1 1 4 5 1 4 输出&#xff1a; 56 66 52 44 54 64 分析题意可以得知&#xff0c;就是接收一串数字&#xff0c;将数字按照下标每次向右移动一位&#xff08;末尾循环到第一位&#xff09;&#xff0c;每次移动玩计算一下下标和数字的乘积且累加。 ①接收…

如何应对“最后时刻任务堆积”(鼓包现象)

应对“最后时刻任务堆积”&#xff08;鼓包现象&#xff09;的方法包括&#xff1a;合理规划项目时间表、强化进度跟踪管理、明确任务优先级、有效的资源配置、提升团队沟通效率。其中&#xff0c;强化进度跟踪管理尤为关键。根据项目管理协会&#xff08;PMI&#xff09;的调查…

19C-19.3环境-impdp导入到view时卡死

帮客户导入一个用户时&#xff0c;发现VIEW部分无法进行下去 Processing object type SCHEMA_EXPORT/TABLE/IDENTITY_COLUMN Processing object type SCHEMA_EXPORT/PACKAGE/PACKAGE_SPEC Processing object type SCHEMA_EXPORT/FUNCTION/FUNCTION Processing object type SCH…

一、简单的 Django 服务

一、配置虚拟环境 1.1 创建一个文件夹在导航栏输入cmd打开 1.2 安装依赖两个库 pip install virtualenv virtualenvwrapper-win -i https://pypi.tuna.tsinghua.edu.cn/simple验证是否安装成功 virtualenv --version pip show virtualenvwrapper-win 1.3 创建虚拟环境 mkvi…

道路运输安全员岗位事项有哪些?

道路运输安全员的岗位事项主要包括以下几个方面&#xff1a; 安全制度与计划 参与制定和完善道路运输企业的安全管理制度、安全操作规程等&#xff0c;确保各项安全工作有章可循。协助制定年度安全工作计划和目标&#xff0c;并负责组织实施和监督执行情况&#xff0c;定期对…