【hello Linux】线程概念
目录
1. 线程概念的铺设
2. Linux线程概念
2.1 什么是线程
2.2 线程的优点
2.3 线程的缺点
2.4 线程异常
2.5 线程用途
3. Linux进程VS线程
4. Linux线程控制
4.1 POSIX线程库
4.2 创建线程
4.3 进程ID和线程ID
4.4 线程终止
4.5 线程等待
4.6 分离线程
Linux🌷
1. 线程概念的铺设
在我们之前的学习中都是一个进程对应一个执行流,也就是说进程是OS分配资源的基本单位,也是CPU调度的基本单位;
今天,我们便要学习一个进程对应一个执行流,或者是一个进程对应多个执行流的情况,这里的每个执行流便可称为一个线程;
也就是说一个进程内是可能存在多个线程的,进程与线程数之比=1:n;内核中有可能存在着大量的线程,OS便要对这些线程进行管理。“先描述再组织”是OS管理对象的一个准则,线程是通过线程控制块(TCB)描述的,这是常规的一些操作系统的做法,比如Windows;
在Linux中是没有专门为线程设计TCB的,而是用进程PCB来模拟线程。
下面用一张图来大概说明下线程与进程:
对于线程来说只创建task_struct,将当前进程的资源(代码+数据)划分为若干份让每个task_struct用,每个task_struct就是一个需要被调度的执行流;对于CPU来说,此时看到的task_struct是小于原先的task_struct的,这里的task_struct也成为轻量级进程;多个线程是共享同一进程的地址空间的;
这样做不用维护复杂的进程和线程的关系,不用单独为线程设计任何算法,直接使用进程的一套相关的方法,OS只需要聚焦在线程间的资源分配上就好了;
进程的今昔对比:
- 之前的进程,内部只有一个执行流;
- 今天的进程,内部可以具有多个执行流;
Linux线程与接口关系的认识:
Linux中的线程是用进程模拟的,Linux中没有提供直接操作线程的接口,只是提供了在同一地址空间内创建task_struct的方法,分配资源给指定的task_struct的接口,这种方法对用户特别不友好,系统级别的工程师在用户层,对Linux轻量级进程接口进行了封装,给我们打包成了库,让用户直接使用库函数,这个库称为 原生线程库 是用户层的;
2. Linux线程概念
2.1 什么是线程
- 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”;
- 一切进程至少都有一个执行线程;
- 线程在进程内部运行,本质是在进程地址空间内运行;
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化;
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流;
2.2 线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多;
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多;
- 线程占用的资源要比进程少很多;
- 能充分利用多处理器的可并行数量;
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务;
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现;
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作;
在这里要注意的一点是:
对于计算密集型应用,并不是说线程越多是越好的,一般线程的个数与CPU的核数相等即可,如果线程太多会导致被过度调度切换(有成本的),假如只有一个CPU划分了10个线程,这样的话还不如让一个进程直接在CPU上运行;
对于IO密集型应用,IO是允许多一些线程,如果一个进程需要等待磁盘资源、IO资源,那么便可以将该进程分为多个线程,让等待这些资源的时间重叠,从而缩短整个等待时间。但也不是说越多越好的,因为磁盘只有一个,划分为再多的线程也只有排队等待同一个磁盘资源,无济于事;
2.3 线程的缺点
一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计
算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指
的是增加了额外的同步和调度开销,而可用的资源不变。
2. 健壮性降低
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
3. 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些 OS 函数会对整个进程造成影响。
编写与调试一个多线程程序比单线程程序困难得多
2.4 线程异常
- 单个线程如果出现除零,野指针等问题导致线程崩溃,进程也会随着崩溃;
- 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出;
2.5 线程用途
- 合理的使用多线程,能提高CPU密集型程序的执行效率;
- 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现);
3. Linux进程VS线程
1. 进程和线程
- 进程是资源分配的基本单位;
- 线程是CPU调度的基本单位;
- 线程共享进程数据,但也拥有自己的一部分数据:
- 线程ID
- 一组寄存器
- 栈
- errno
- 信号屏蔽字
- 调度优先级
在这最重要的是:栈和上下文;
栈保存临时数据,上下文用于CPU的调度切换;
2. 进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
- 文件描述符表;
- 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数);
- 当前工作目录;
- 用户id和组id;

4. Linux线程控制
4.1 POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的;
- 要使用这些函数库,要通过引入头文<pthread.h>;
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项;
4.2 创建线程
功能:创建一个新的线程原型
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;失败返回错误码
- 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误;
- pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回;
- pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值来判定,因为读取返回值要比读取线程内的errno变量的开销更小;
#include <stdio.h>#include <pthread.h>#include <string.h>#include <unistd.h>#include <sys/types.h>void *thread_run(void *arg){int i;for(;;){printf("I am %s,pid:%d\n",(char*)arg,getpid()); sleep(1);}}int main(){//用于接收新创建的线程的idpthread_t tid;//用于接收创建线程函数的返回值int ret;//创建线程if((ret=pthread_create(&tid,NULL,thread_run,(void*)"thread1"))!=0){printf("pthread_create:%s\n",strerror(ret));return 1;}//主执行流int i;for(;;){printf("I am main thread!pid:%d\n",getpid());sleep(1);}return 0;}
4.3 进程ID和线程ID
- 在Linux中,目前的线程实现是Native POSIX Thread Libaray,简称NPTL。在这种实现下,线程又被称为轻量级进程(Light Weighted Process),每一个用户态的线程,在内核中都对应一个调度实体,也拥有自己的进程描述符(task_struct结构体)。
- 没有线程之前,一个进程对应内核里的一个进程描述符,对应一个进程ID。但是引入线程概念之后,情况发生了变化,一个用户进程下管辖N个用户态线程,每个线程作为一个独立的调度实体在内核态都有自己的进程描述符,进程和内核的描述符一下子就变成了1:N关系,POSIX标准又要求进程内的所有线程调用getpid函数时返回相同的进程ID,如何解决上述问题呢?
- Linux内核引入了线程组的概念
struct task_struct {...pid_t pid;pid_t tgid;...struct task_struct *group_leader;...struct list_head thread_group;...
};
- 多线程的进程,又被称为线程组,线程组内的每一个线程在内核之中都存在一个进程描述符(task_struct)与之对应。进程描述符结构体中的pid,表面上看对应的是进程ID,其实不然,它对应的是线程ID;进程描述符中的tgid,含义是Thread Group ID,该值对应的是用户层面的进程ID;
查看线程ID:
ps -aL
强调一点:
线程和进程不一样,进程有父进程的概念,但在线程组里面,所有的线程都是对等关系。
在程序中,我们也可以使用函数的方式获得该线程自身的ID:
#include <pthread.h>pthread_t pthread_self(void);
该函数获得的线程ID和刚才查出的线程ID是不同的,我们查到的线程ID是 pthread 线程库的线程ID,使用命令的方式查看的是Linux内核中的LWP,pthread库的线程ID是一个虚拟地址,如下图中的pthread_t id所示;
4.4 线程终止
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit(终止整个进程);
- 线程可以调用pthread_ exit终止自己;
- 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程;
功能:线程终止原型:void pthread_exit(void *value_ptr);参数:value_ptr:value_ptr不要指向一个局部变量。返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
功能:取消一个执行中的线程原型:int pthread_cancel(pthread_t thread);参数:thread:线程ID返回值:成功返回0;失败返回错误码
4.5 线程等待
线程等待的原因:
- 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
- 创建新的线程不会复用刚才退出线程的地址空间。
pthread_join函数:
功能:等待线程结束原型:int pthread_join(pthread_t thread, void **value_ptr);参数:thread:线程ID;value_ptr:输出型参数,用来获取新线程推出时候的函数的返回值;返回值:成功返回0;失败返回错误码
- 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值;
- 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED((void*)-1);
- 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数;
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数;

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> void *thread1( void *arg ) { printf("thread 1 returning ... \n"); int *p = (int*)malloc(sizeof(int)); *p = 1; return (void*)p; } void *thread2( void *arg ) { printf("thread 2 exiting ...\n"); int *p = (int*)malloc(sizeof(int)); *p = 2; pthread_exit((void*)p); } void *thread3( void *arg ) { while(1) { printf("thread 3 is running ...\n"); sleep(1); } return NULL; } int main() { pthread_t tid; void *ret; //thread1 return //创建线程1 pthread_create(&tid, NULL, thread1, NULL); //等待线程1 pthread_join(tid,&ret); printf("thread return, thread id:%X, return code:%d\n",tid,*(int*)ret); free(ret); //thread2 exit //创建线程2 pthread_create(&tid, NULL, thread2, NULL); //等待线程2pthread_join(tid,&ret);printf("thread return, thread id:%X, return code:%d\n",tid,*(int*)ret);free(ret);//thread3 cancel by other//创建线程3pthread_create(&tid, NULL, thread3, NULL);sleep(3);pthread_cancel(tid);pthread_join(tid,&ret);if(ret==PTHREAD_CANCELED)printf("thread return, thread id:%X, return code:PTHREAD_CANELED\n");else printf("thread return, thread id:%X, return code:NULL\n",tid);return 0;}

线程对于整个程序来说,根本上也是用函数的形式呈现的;
函数退出时有三种情况:
- 1. 代码跑完结果正确;
- 2. 代码跑完结果不正确;
- 3. 代码异常;
对于上述3种情况,我们只 join 前两种,因为线程代码异常的话,OS会发信号给进程,整个进程就瘫痪了,无需考虑此情况;
4.6 分离线程
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏;
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* thread_run(void *arg)
{pthread_detach(pthread_self());printf("%s\n",(char*)arg);return NULL;
}int main()
{pthread_t tid;if(pthread_create(&tid, NULL, thread_run, (void*)"thread1")!=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;
}
如果本篇博客对您有所收获的话,还请点赞👍、收藏🤏加关注🎈
相关文章:

【hello Linux】线程概念
目录 1. 线程概念的铺设 2. Linux线程概念 2.1 什么是线程 2.2 线程的优点 2.3 线程的缺点 2.4 线程异常 2.5 线程用途 3. Linux进程VS线程 4. Linux线程控制 4.1 POSIX线程库 4.2 创建线程 4.3 进程ID和线程ID 4.4 线程终止 4.5 线程等待 4.6 分离线程 Linux🌷 1…...

JavaWeb07(MVC应用01[家居商城]连接数据库)
目录 一.什么是MVC设计模式? 1.2 MVC设计模式有什么优点? 二.MVC运用(家居商城) 2.1 实现登录 2.2 绑定轮播【随机三个商品】 2.2.1 效果预览 index.jsp 2.3 绑定最新上架&热门家居 2.3.1 效果预览 2.3.2 代码实现 数据…...
如何使用电商API接口API接口如何应用
使用API接口 API(应用程序接口)是现代软件开发中必不可少的一部分,它通常允许软件与其他软件或服务进行交互。使用API可以大大提高软件的灵活性和可扩展性,并允许您轻松添加新的功能和服务,因此,API接口的…...

【移动端网页布局】流式布局案例 ⑥ ( 多排按钮导航栏 | 设置浮动及宽度 | 设置图片样式 | 设置文本 )
文章目录 一、多排按钮导航栏样式及核心要点1、实现效果2、总体布局设计3、设置浮动及宽度4、设置图片样式5、设置文本 二、完整代码实例1、HTML 标签结构2、CSS 样式3、展示效果 一、多排按钮导航栏样式及核心要点 1、实现效果 要实现下面的导航栏效果 ; 2、总体布局设计 该导…...

1. 先从云计算讲起
本章讲解知识点 什么是云计算? 为什么要用云计算? 物理服务器与云服务器对比 云计算服务类型 云计算部署类型 1. 什么是云计算? 云计算是一种通过计算机网络以服务的方式提供动态可伸缩的虚拟化资源的计算模式。按照服务层次分为IaaS、…...

ZooKeeper安装与配置集群
简介: ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,它提供了一个分布式环境中的高可用性、高性能、有序访问的数据存储,可以让分布式应用程…...
浅谈Mysql的RR和RC隔离级别的主要区别
MySQL默认为RR级别 首先默认RR是因为mysql为了保证在主从同步过程中数据的安全的问题(涉及到binlog三种格式)。 就是说两个并发事务数AB,A先开启事物最后提交也是最后,事务B开启和提交都在A内部,由于隔离级别不同&…...
Build生成器模式
设计模式简述 设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便地复用成功的设计和体系结构。 生成器模式(创建型设计模式) 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以…...

C++程序设计——常见C++11新特性
一、列表初始化 1.C98中{}的初始化问题 在C98中,允许使用花括号{}对数组元素进行统一的列表初始化值设定,比如: 但是对于一些自定义类型,就无法使用这样的方式进行初始化了,比如: 就无法通过编译ÿ…...
Rust main 函数返回值类型不能是 String
是的,Rust 的 main 函数返回值类型不能是 String。 Rust 的 main 函数只能返回以下几种类型之一: ():表示空类型,不返回任何值。i32:表示程序的退出码,通常非零值表示执行失败,0 表示执行成功…...

视频里的音乐怎么转换成mp3格式?
视频里的音乐怎么转换成mp3格式?视频里的音乐转换为mp3的原因有很多,主要是因为mp3格式是一种音频格式,文件大小较小,更易于存储和传输。相比之下,视频格式则是一种视频文件格式,虽然包含音频,但…...

CSS3 grid网格布局
文章目录 CSS3 grid网格布局概述grid属性说明使用grid-template-rows & grid-template-columns 定义行高和列宽grid-auto-flow 定义项目的排列顺序grid-auto-rows & grid-auto-columns 定义多余网格的行高和列宽row-gap & column-gap 设置行间距和列间距gap 简写形…...

SPSS如何进行均值比较和T检验之案例实训?
文章目录 0.引言1.均值过程2.单样本T检验3.独立样本T检验4.成对样本T检验 0.引言 因科研等多场景需要进行数据统计分析,笔者对SPSS进行了学习,本文通过《SPSS统计分析从入门到精通》及其配套素材结合网上相关资料进行学习笔记总结,本文对均值…...

Packet Tracer - 配置交换机端口安全
Packet Tracer - 配置交换机端口安全 地址分配表 设备 接口 IP 地址 子网掩码 S1 VLAN 1 10.10.10.2 255.255.255.0 PC1 NIC 10.10.10.10 255.255.255.0 PC2 NIC 10.10.10.11 255.255.255.0 非法笔记本电脑 NIC 10.10.10.12 255.255.255.0 目标 第 1 部…...

一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, 资料整理+笔记(大全)
本文由 大侠(AhcaoZhu)原创,转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, 资料整理笔记(大全) 摘要模块图类关系图模块全展开【aiohttp】统计常量模块1 aiohttp.hd…...

Linux + 香橙派 + V4L2 + http 实现远程监控摄像头在网页端显示
项目场景: 项目需求,需要做一个基于边缘端的人脸识别远程监控摄像头并在网页前端展示 ,这里采用国产香橙派作为边缘计算终端,安装ubuntu系统,系统中采用v4l2接口对摄像头进行获取,当客户端通过网页进行请求…...

《编码——隐匿在计算机软硬件背后的语言》精炼——第15-16章(十六进制,RAM)
“学习如春起之苗,不见其增,日有所长。” —— 宋代朱熹 文章目录 十六进制十六进制概述十六进制表字节到十六进制 存储器特定的读功能特定的写功能RAM大型RAM阵列 十六进制 十六进制概述 十六进制是一种适用于计算机的进制法。在十进制中,…...
leetcode.1376 通知所有员工所需的时间 - bfs/dfs + 树
1376. 通知所有员工所需的时间 目录 一、bfs 二、dfs 题目: 公司里有 n 名员工,每个员工的 ID 都是独一无二的,编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。在 manager 数组中,每个员工都有一个直属负责人&#x…...

AtCoder Beginner Contest 300——A-G题讲解
蒟蒻来讲题,还望大家喜。若哪有问题,大家尽可提! Hello, 大家好哇!本初中生蒟蒻讲解一下AtCoder Beginner Contest 300这场比赛的A-G题! A - N-choice question 原题 Problem Statement Given integers A A A and…...

Go:值与指针
1. 计算机中的值 在百万年的演化历史中,人类对事物的属性进行了抽象,有了数量、精度、信息等概念的表示,对应的我们称之为整数、小数、文本文字等。计算机出现后,我们使用计算机对真实世界的问题进行建模,通过计算机的…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...