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

news2025/1/6 19:12:44

😁博客主页😁:🚀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环境编程:从应用到内核》

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

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

相关文章

MySQL语法分类 DQL(1)基础查询

//语法 select 字段列表 from 表名列表 where条件列表 group by分组字段 having 分组后的条件 order by排序 limit 分页限定为了更好的学习这里给出基本表数据用于查询操作 create table student (id int, name varchar(20), age int, sex varchar(5),address varchar(100),ma…

Go语言加密技术实战:掌握encoding/pem库

Go语言加密技术实战&#xff1a;掌握encoding/pem库 引言PEM格式简介核心组成常见用途 Go语言的encoding/pem库概览核心功能使用场景 开始使用encoding/pem读取PEM文件编码为PEM格式 深入理解PEM编码自定义PEM头部信息 使用encoding/pem解码PEM文件PEM文件的加密与解密加密私钥…

代码随想录训练营Day25:● 216.组合总和III ● 17.电话号码的字母组合

216.组合总和III 题目链接 https://leetcode.cn/problems/combination-sum-iii/description/ 题目描述 思路 自己写的效率会慢一些&#xff0c;而且没有用到剪枝 class Solution {List<List<Integer>> list new ArrayList<>();List<Integer> lis…

基于PIESDK的二次开发--土壤水反演系统

目录 系统演示数据获取算法封装系统 系统演示 数据获取 基于TVDI的土壤水分反演需要有地表温度和植被指数数据&#xff0c;该部分参考Landsat计算TVDI进行干旱监测&#xff08;二&#xff09; 得到两张TIF影像 算法封装 初始的.py代码参数是直接指定的&#xff0c;然而在封…

【Javascript编程实操06】1、反转数组和字符串 2、将二维数组转一维数组

前言 1、反转数组和字符串 代码&#xff1a; 实现效果&#xff1a; 2、将二维数组转一维数组 代码&#xff1a; 实现效果&#xff1a; 总结 前言 本次主要是针对Javascript阶段的字符串与数组的实操练习&#xff0c;共有2个实操&#xff0c;大家可以在实操的过程中更加深…

ARM 寄存器学习:(一)arm多种模式下得寄存器

一.ARM7种状态以及每种状态的寄存器&#xff1a; ARM 处理器共有 7 种不同的处理器模式&#xff0c;在每一种处理器模式中可见的寄存器包括 15 个通用寄存器( R0~R14)、一个或两个(User和Sys不是异常模式&#xff0c;没有spsr寄存器)状态寄存器&#xff08;cpsr和spsr&…

想兼职赚钱?盘点6个靠谱兼职,赚钱更轻松!

1&#xff0c;微头条搬砖 微头条搬砖是一个门槛不高的赚钱方式&#xff0c;而且不需要你有多么好的原创能力&#xff0c;去收集一些热门文章的素材进行文章伪原创&#xff0c;十分钟就能搞定&#xff0c;只要你的文章有爆点&#xff0c;足够吸人眼球&#xff0c;就能够获取不低…

Web核心,HTTP,tomcat,Servlet

1&#xff0c;JavaWeb技术栈 B/S架构:Browser/Server&#xff0c;浏览器/服务器架构模式&#xff0c;它的特点是&#xff0c;客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器&#xff0c;获取Web资源&#xff0c;服务器把Web资源…

Linux服务器(Debian系)包含UOS安全相关巡检shell脚本

#!/bin/bash# Define output file current_date$(date "%Y%m%d") # Gets the current date in YYYYMMDD format output_file"server_security_inspection_report_${current_date}.txt"# Empty the file initially echo > $output_file# 获取巡检时间 (…

蓝桥杯刷题(九)

1.三国游戏 代码 #输入数据 nint(input()) Xlilist(map(int,input().split())) Ylilist(map(int,input().split())) Zlilist(map(int,input().split())) #分别计算X-Y-Z/Y-Z-X/Z-X-Y并排序 newXli sorted([Xli[i] - Yli[i] - Zli[i] for i in range(n)],reverseTrue) newYli …

Pikachu 靶场搭建

文章目录 环境说明1 Pikachu 简介2 Pikachu 安装 环境说明 操作系统&#xff1a;Windows 10PHPStudy 版本: 8.1.1.3Apache 版本&#xff1a;2.4.39MySQL 版本 5.7.26 1 Pikachu 简介 Pikachu是一个使用“PHP MySQL” 开发、包含常见的Web安全漏洞、适合Web渗透测试学习人员练…

腾讯云轻量2核4G5M服务器卡不卡?性能怎么样?

腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;5M带宽下载速度峰值可达640KB/秒&#xff0c;阿腾云以搭建网站为例&#xff0c;假设优化后平均大小为60KB&#xff0c;则5M带宽可支撑10个用户同时在1秒内打开网站&#xff0c;并发数为10&#xff0c;经阿腾云测试&a…

C++算法学习心得八.动态规划算法(4)

1.零钱兑换&#xff08;322题&#xff09; 题目描述&#xff1a; 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1。 你可以认为每种硬币的数量是无限的。…

劲仔食品三年倍增,抢先打响鹌鹑蛋“健康”属性品牌之争?

如果说&#xff0c;进入2024年后&#xff0c;在股价继续陷入回调状态的食品板块中有个股走势表现相对亮眼&#xff0c;那么劲仔食品必是其中之一。 从去年发布2023年三季度业绩公告以来&#xff0c;其强劲的业绩表现就带动了股价走出小趋势。2023年10月23日至今2024年3月13日收…

VTK安装(C++)并配置vs

准备工作&#xff1a; 1.VTK下载包(此教程使用VTK8.2.0) 2.CMAKE(此教程使用3.29.0) 在此不过多赘述&#xff0c;可在网上搜索cmake安装 3.visual studio(此教程使用vs2019) VTK下载及编译&#xff1a; 1、找到自己适合的VTK版本,我选择的是VTK8.2.0。 1.1 官网下载&#xff…

向量相似性度量的常用方法

向量相似性度量的常用方法 0. 引言1. 欧氏距离(Euclidean distance)2. 余弦相似度(Cosine similarity)3. 汉明距离(Hamming distance)4. 点积相似度 (Dot Product Similarity)5. 曼哈顿距离 (Manhattan Distance) 0. 引言 今天花时间学习学习向量相似性度量的常用方法&#xf…

c语言按位与,按位或,按位异或,按位取反

1、按位与& 按位与的实现逻辑是相同为1&#xff0c;相异为0&#xff1b; 2、按位或 | 按位或的实现逻辑是有1为1&#xff0c;无一为0&#xff1b; 3、按位异或 ^ 按位或的实现逻辑是相同为0&#xff0c;相异为1&#xff1b; 4、按位取反 ~ 按位取反的实现逻辑是0改1&am…

Postman-Installation has failed

如图&#xff1a; 解决方法&#xff1a; 打开文件夹 Postman-win64-Setup 点击Postman.exe 即可

Nginx高级技术: 代理缓存配置

一、缓存说明 Nginx缓存&#xff0c;Nginx 提供了一个强大的反向代理和 HTTP 服务器功能&#xff0c;同时也是一个高效的缓存服务器。一般情况下系统用到的缓存有以下三种&#xff1a; 1、服务端缓存&#xff1a;缓存存在后端服务器&#xff0c;如 redis。 2、代理缓存&#…

L1-5 试试手气 【Java】

我们知道一个骰子有 6 个面&#xff0c;分别刻了 1 到 6 个点。下面给你 6 个骰子的初始状态&#xff0c;即它们朝上一面的点数&#xff0c;让你一把抓起摇出另一套结果。假设你摇骰子的手段特别精妙&#xff0c;每次摇出的结果都满足以下两个条件&#xff1a; 1、每个骰子摇出…