【Linux C | 多线程编程】线程的基础知识

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍线程的基础知识 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:2024-03-16 13:03:47

本文未经允许,不得转发!!!

目录

  • 🎄一、什么是线程
  • 🎄二、多线程的优缺点
    • ✨2.1 多线程的优点
    • ✨2.2 多线程的缺点
  • 🎄三、线程ID
    • ✨3.1 gettid
    • ✨3.2 syscall
    • ✨3.3 查看线程ID
  • 🎄四、/proc/PID/task/ 目录
  • 🎄五、总结


在这里插入图片描述

🎄一、什么是线程

在了解线程之前,先看看程序和进程是什么?

程序:程序或可执行文件是静态的静态的实体,只是一组指令的集合,没有执行的意义。

进程:是运行之后的程序,是一个动态的实体, 有自己的生命周期。

线程:线程是一个进程内部的一个控制序列,是操作系统进程调度器可以调度的最小执行单元。一个进程运行之后,就至少会有一个线程。只有一个线程的进程叫单线程进程

一个进程可能包含多个线程, 传统意义上的进程, 不过是多线程的一种特例, 即该进程只包含一个线程。在Linux系统开发中,也经常会使用到多线程编程,把进程设计成在同一时刻能够做多件事,每个线程处理各自独立的任务。

在这里插入图片描述


在这里插入图片描述

🎄二、多线程的优缺点

✨2.1 多线程的优点

  1. 同一个进程的线程会共享内存地址空间。同一个进程的多个线程共享一份全局内存区域, 包括初始化数据段、 未初始化数据段和动态分配的堆内存段。这使得创建或终止线程的时间要少于进程,共享数据比进程简单。
  2. 发挥多核优势, 充分利用CPU资源。如果存在多个相同的任务, 彼此之间并行不悖, 互不依赖(或者依赖性很小) , 那么启动多个线程并发处理, 是一个不错的选择。通过为每种事件类型的处理分配单独的线程,能够简化处理异步时间的代码。
  3. 有些问题可以通过将其分解从而改善整个程序的吞吐量。
  4. 交互的程序可以通过使用多线程实现相应时间的改善,多线程可以把程序中处理用户输入输出的部分与其它部分分开。

✨2.2 多线程的缺点

  1. 多线程的进程, 因地址空间的共享让该进程变得更加脆弱。多个线程之中, 只要有一个线程不够健壮存在bug,就可能导致整个进程崩溃。
  2. 线程模型作为一种并发的编程模型, 效率并没有想象的那么高, 会出现复杂度高、 易出错、 难以测试和定位的问题。
    多线程编程很难将全部任务均等地分给每个进程;
    多线程之间可能存在依赖关系,一个线程未完成某些操作之前,其他线程不应该运行。
  3. 多线程编程存在四个陷进:死锁(Dead Lock)、饿死(Starvation)、活锁(Live Lock)、竞态条件(Race Condition)

在这里插入图片描述

🎄三、线程ID

在Linux中, 目前的线程实现是Native POSIX Thread Library,简称NPTL。在这种实现下,线程又被称为轻量级进程(Light Weighted Process),每一个用户态的线程,在内核之中都对应一个调度实体,也拥有自己的进程描述符(task_struct结构体) 。

对于单线程进程来说,一个进程对应内核里的一个进程描述符, 对应一个进程ID。
多线程的进程, 又被称为线程组, 线程组内的每一个线程在内核之中都存在一个进程描述符(task_struct) 与之对应。

struct task_struct {...
	pid_t pid;
	pid_t tgid;
	...
	struct task_struct *group_leader;
	...
	struct list_head thread_group;
	...
}

内核的 struct task_struct 结构体中的两个ID字段:
pid:pthread ID,表示线程ID;
tgid:意思是 Thread Group ID,表示线程组ID,对应的是进程ID。

✨3.1 gettid

Linux中提供系统调用 gettid 来获取调用者的线程ID。但是,这个系统调用没有glibc封装,使用glibc的编译器时只能使用 syscall 函数来调用 gettid 系统调用;

gettid函数原型

#include <sys/types.h>
pid_t gettid(void);
Note: There is no glibc wrapper for this system call; see NOTES.

函数描述:gettid() 返回调用方的线程ID(TID)。在单线程进程中,线程ID等于进程ID(PID,由getpid()返回)。在多线程进程中,所有线程都有相同的PID,但每个线程都有一个唯一的TID。


✨3.2 syscall

因为在 glibc 的编译器没有gettid 函数,所以只能使用 syscall 函数来获取线程ID,syscall函数原型如下:

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>   /* For SYS_xxx definitions */
int syscall(int number, ...);

函数描述:syscall()是一个小型库函数,用于系统调用,该系统调用的汇编语言接口具有指定的数字和指定的参数。例如,当调用C库中没有包装函数的系统调用时,使用syscall()非常有用。

下面是一个例子,创建4个线程,分别获取自己的线程ID,例子中用到一些陌生的函数如:pthread_create,会在后面介绍线程的文章讲解。这个例子可以使用命令gcc gettid.c -lpthread 进行编译,-lpthread表示要链接线程库。

// gettid.c
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>

void *func(void *arg)
{
	int *pI = arg;
	pid_t tid = /*getpid()*/syscall(SYS_gettid);
	printf("this thread is num %d , tid=%d\n", *pI, tid);
	while(1); // 避免线程退出
	return NULL;
}
int main()
{
	int i=0;
	int args[4] = {0,};
	for(i=0; i<4; i++)
	{
		pthread_t threadId;
		args[i] = i;
		pthread_create(&threadId, NULL, func, &args[i]);
	}
	while(1); // 避免线程退出
	return 0;
}

运行结果:
在这里插入图片描述


✨3.3 查看线程ID

在Linux下,使用ps命令中的-L选项,会显示出线程的如下信息。

  • LWP: 线程ID, 即gettid() 系统调用的返回值。
  • NLWP: 线程组内线程的个数

使用ps命令查看上面程序的线程,因为ps命令输出太多了,这里使用grep过滤一下,使用命令ps -eLf | grep -e "PID" -e "a.out",只显示带有PIDa.out关键字的行,结果如下:
可以看到a.out有5个线程,主线程是PID和LWP列一样的线程,即113178那一行。
在这里插入图片描述
ps命令还可以使用-T选项来显示SPID列,表示线程ID。如果想知道更多的ps命令,可以使用ps --help查看。


在这里插入图片描述

🎄四、/proc/PID/task/ 目录

procfs在task下会给进程的每个线程建立一个子目录, 目录名为线程ID。

已知某进程的进程ID,我们就可以通过/proc/PID/task/目录下的子目录来查看该进程的线程个数和线程ID。

以上个小节进程ID为113178的进程举例,也是可以看到对应目录下有5个由线程ID命令的子目录:
在这里插入图片描述


在这里插入图片描述

🎄五、总结

👉本文介绍线程的基础知识,包括线程优缺点、线程ID、/proc/PID/task/ 目录等。

需要强调的一点是,线程和进程不一样, 进程有父进程的概念, 但在线程组里面, 所有的线程都是对等的关系。

  • 并不是只有主线程才能创建线程, 被创建出来的线程同样可以创建线程。
  • 不存在类似于fork函数那样的父子关系, 大家都归属于同一个线程组, 进程ID都相等, group_leader都指向主线程, 而且各有各的线程ID。
  • 并非只有主线程才能调用pthread_join连接其他线程, 同一线程组内的任意线程都可以对某线程执行pthread_join函数。
  • 并非只有主线程才能调用pthread_detach函数, 其实任意线程都可以对同一线程组内的线程执行分离操作

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
《Linux环境编程:从应用到内核》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue 3 + TypeScript 项目中全局挂载并使用工具函数

一、proxy方式 1.封装日期选择工具函数&#xff1a; 在untils文件夹下新建index.ts,并导出工具函数 /*** 获取不同类型日期* param&#xff1a;类型 dateVal: 是否指定*/ export function getSystemDate(param: any, dateVal: any) {let systemDate dateVal ? new Date(da…

Oracle Primavera P6 数据库升级

前言 为了模拟各种P6测试&#xff0c;我常常会安装各种不同版本的p6系统&#xff0c;无论是P6服务&#xff0c;亦或是P6客户端工具Professional&#xff0c;在今天操作p6使用时&#xff0c;无意识到安装在本地的P6 数据库&#xff08;21.12&#xff09;出现了与Professional软…

双指针算法_移动零_

题目&#xff1a; 给定一个数组 num &#xff0c;编写一个函数将数组内部的数字0都移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序&#xff01; 同时不能通过复制数组&#xff0c;开辟新的数组空间的情况下原地对数组进行操作 示例&#xff1a; 本题的原理&#x…

Python控制摄像头并获取数据文件

一、引言 摄像头作为计算机视觉领域的核心设备之一&#xff0c;广泛应用于视频监控、图像采集和数据处理等领域。通过Python编程语言&#xff0c;我们可以实现对摄像头的精确控制&#xff0c;包括摄像头的开启、关闭、参数设置以及数据获取等功能。 目录 一、引言 二、摄像头…

【上海大学计算机组成原理实验报告】一、数据传送实验

一、实验目的 了解实验仪器数据总线的控制方式。掌握数据传送的基本原理。掌握各寄存器的结构、工作原理及其控制方法。 二、实验原理 根据实验指导书的相关内容&#xff0c;数据输入到寄存器的过程是先通过指令选择源和目标&#xff0c;再通过数据总线来传送数据&#xff0…

探索Webpack中的常见Loader

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

yolov8 分割 模型 网络 模块图

下图是使用yolov8n-seg-p6.yaml imgsz1472 类别数2的情况下训练得到的静态导出的onnx文件使用netron工具可视化的结果 简单标注了yolov8n-seg-p6.yaml配置文件中各层和netron工具可视化的结果的对应关系(head 中的 Segment除外)

ideaSSM失物招领管理系统网页模式开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea ssm 失物招领管理系统是一套完善的完整信息管理系统&#xff0c;结合SSM框架完成本系统SpringMVC spring mybatis &#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数…

yocto系列之构建与运行第一个镜像

回顾 在前面的文章中&#xff0c;我们介绍了Yocto&#xff0c;并展示了如何在Ubuntu PC上进行Yocto构建。这里是对应的链接&#xff1a; Yocto: 第1部分 - yocto系列之yocto是个什么东东 https://mp.csdn.net/mp_blog/creation/editor/136742286 Yocto: 第2部分 - yocto系列…

Linux学习(6)——Linux环境变量

1.环境变量简介 通过设置环境变量&#xff0c;可以满足不同用户的需求。 直接定义的变量是全局变量&#xff0c;不是环境变量。shell进程的子进程无法访问这种变量。 用export命令可以将全局变量导出为环境变量。子进程就可以访问了。 &#xff08;虽然用export命令修饰后&…

闲聊电脑(7)常见故障排查

闲聊电脑&#xff08;7&#xff09;常见故障排查 夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;平时遇到电脑故障该咋处理呢&#xff1f; 冰箱&#xf…

数据结构——动态顺序表

数据结构的动态顺序表有以下几个操作&#xff1a;创建&#xff0c;销毁&#xff0c;初始化&#xff0c;增删查改和打印以及内存空间不够时的扩容 本文的宏定义&#xff1a; #define SeqTypeData int 1.动态顺序表的创建 typedef struct SeqListInit{//动态顺序表的创建SeqT…

自学rabbitmq入门到精通

交换机的fault &#xff08;发布与订阅模式&#xff09; 因为消息是由生产者发送给excahnge&#xff0c;exchange发送给队列&#xff0c; 然后由队列发送给消费者的。 展示使用图形化界面使用fanout模式。 创建交换机 然后创建三个队列&#xff0c;绑定对应的交换机&#xff…

docker的常用指令

docker的常用指令 从docker镜像仓库&#xff0c;搜索所有和mysql有关的镜像 docker search mysql 从docker仓库拉取mysql docker pull mysql这里的mysql是指使用search搜索出来的所有容器的NAME 如果和我一样遇到以下问题&#xff1a; 我可以登录阿里云的官网&#xff0c;找…

[mysql面试必备技能]-一条SQL的执行过程

天天和数据库打交道&#xff0c;一天能写上几十条 SQL 语句&#xff0c;但你知道我们的系统是如何和数据库交互的吗&#xff1f;MySQL 如何帮我们存储数据、又是如何帮我们管理事务&#xff1f;....是不是感觉真的除了写几个 「select * from dual」外基本脑子一片空白&#xf…

使用Python打造一款摸鱼倒计时界面

目录 一、引言 二、需求分析 三、技术选型 四、代码实现 导入必要的库和模块 创建主窗口 添加倒计时设置和显示组件 实现倒计时逻辑 运行主循环 五、案例测试与优化 六、总结 一、引言 在日常的工作和生活中&#xff0c;我们经常会遇到需要暂时离开工作岗位的情况&…

Docker容器化技术(使用Dockerfile制作镜像)

Docker中的镜像分层 Docker 支持通过扩展现有镜像&#xff0c;创建新的镜像。实际上&#xff0c;Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。 1、Docker 镜像为什么分层 镜像分层最大的一个好处就是共享资源。 比如说有多个镜像都从相…

springboot“力炫”健身馆网站

摘要 随着网络科技的不断发展以及人们经济水平的逐步提高&#xff0c;网络技术如今已成为人们生活中不可缺少的一部分&#xff0c;而信息管理系统是通过计算机技术&#xff0c;针对用户需求开发与设计&#xff0c;该技术尤其在各行业领域发挥了巨大的作用&#xff0c;有效地促…

当前组件端口莫名增加127.0.0.1:3658和8563

当部署组件到服务器中&#xff0c;可以通过下方的命令查询服务pid占用的端口&#xff0c; netstat -nap |grep PID | grep LISTEN查询之后发现除了自己组件的端口还增加 百思不得其解后&#xff0c;知道了3658 8563端口是近期使用的arthas组件的端口&#xff0c; 启动arthas组…

ROS——集成开发环境搭建

1.4 ROS集成开发环境搭建 和大多数开发环境一样&#xff0c;理论上&#xff0c;在 ROS 中&#xff0c;只需要记事本就可以编写基本的 ROS 程序&#xff0c;但是工欲善其事必先利其器&#xff0c;为了提高开发效率&#xff0c;可以先安装集成开发工具和使用方便的工具:终端、ID…