Linux 线程概念

文章目录

  • 前言
  • 线程的概念
  • 线程的操作
  • 操作的原理
  • 补充与说明

前言

① 函数的具体说明被放在补充与说明部分
② 只说些基础概念和函数使用

线程的概念

网络回答:Linux 线程是指在 Linux 操作系统中创建和管理的轻量级执行单元。线程是进程的一部分,与进程共享同一地址空间和文件描述符等资源,但拥有独立的程序计数器、栈和寄存器等执行上下文。线程可以并发执行,实现多任务处理。

个人理解:Linux中,进程是承担资源分配的实体,换一种说法,一个进程占用一部分硬件资源。 CPU调度进程,或者说运行进程依靠的是task_struct来访问进程所占用的资源。所以在CPU视角来看,task_struct就是CPU识别"进程"的唯一信息。 如果给在一个进程内创建多个task_struct, CPU就会认为这些task_struct是不同的“进程“(这就是轻量级进程), 会把CPU算力资源分配给这些task_struct, 但实际上这些task_struct对应都是同一进程资源。 其实Linux没有真正意义上的线程结构,Linux是利用PCB结构模拟的线程。 也就是说Linux中的轻量级进程就是线程。 也因此,在后续Linux线程操作的学习中可以发现:Linux没有系统接口,而是在用户层提供了pthread原生线程库。
在这里插入图片描述

线程的操作

//创建一个新的线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) 
//获取本线程的线程ID,这里的线程ID指的是pthread_t类型
pthread_t pthread_self(void);
//终止线程。value_ptr为传递给回收已终止线程的线程
void pthread_exit(void *value_ptr);
//“取消”一个执行中的线程
int pthread_cancel(pthread_t thread);
//线程等待,用于回收已经退出的线程
int pthread_join(pthread_t thread, void **value_ptr);
//线程分离,和 pthread_join 不同的是不关心线程的返回值	
int pthread_detach(pthread_t thread);

使用范例:

#include <pthread.h>
#include <stdio.h>void* thread_func(void* arg) {int thread_num = *(int*)arg;printf("Thread %d is running\n", thread_num);pthread_exit(NULL);
}int main() {pthread_t thread;int thread_arg = 1;// 创建线程int ret = pthread_create(&thread, NULL, thread_func, &thread_arg);if (ret != 0) {printf("Failed to create thread\n");return 1;}// 获取本线程的线程IDpthread_t self_thread = pthread_self();printf("Self thread ID: %lu\n", self_thread);// 等待线程结束并回收资源void* thread_result;ret = pthread_join(thread, &thread_result);if (ret != 0) {printf("Failed to join thread\n");return 1;}// 分离线程/*一个线程不能既是分离的又是joinable的ret = pthread_detach(thread);if (ret != 0) {printf("Failed to detach thread\n");return 1;}*/printf("Main thread is exiting\n");return 0;
}

操作的原理

  1. 线程组

在线程的概念部分解释到,①一个进程里可能有多个线程②Linux无实际的线程,我们使用的线程函数实际来自于位于用户层的原生线程库。 所以这里引入线程组的概念,线程组 = 多线程的进程 ;

struct task_struct {
...
pid_t pid;   // 线程ID
pid_t tgid;  // 线程组ID  //也就是ps -l 指令看到的PID
...
struct task_struct *group_leader;
...
struct list_head thread_group;
...
};

ps -eLf 指令的结果PID对应结构体中的tgid, LWP对应pid ;
另外,NLWP为线程组内线程的个数,线程ID(LWP)和进程ID(PID)相同的线程为主线程。
在这里插入图片描述

  1. 线程ID

pthread_t pthread_self(void) 函数返回的pthread_t和上述的LWP不是一个东西。因为Linux无实际的线程,我们使用的线程函数实际来自于位于用户层的原生线程库(动态库),所以实际CPU调度过程为:CPU -> task_struct -> 进程地址空间 -> 加载在内存中的动态线程(内存资源) 。 pthread_t类型实际为task_struct通过进程地址空间访问共享动态库指定位置的指针。 原理图如下:
在这里插入图片描述
所以综上所述LWP值得是task_struct结构体中的一个属性,而pthread_t是task_struct通过进程地址空间访问共享动态库指定位置的指针,它们口头上都被称为线程ID,但本质有很大区别。(pthread_t主要在写代码时被称为线程ID)。

  1. pthread_cancel() 和 pthread_exit() 的区别

pthread_cancel()函数用于取消指定线程的执行。当调用pthread_cancel()函数时,它会发送一个取消请求给指定线程,但线程是否真正被取消取决于线程内部的取消点。线程可以在取消点处检查取消请求并决定是否继续执行或者终止。这个函数可以在任何线程中调用,包括主线程。

pthread_exit()函数用于终止当前线程的执行,并返回一个指定的退出码。当调用pthread_exit()函数时,当前线程会立即终止,并且不会继续执行后续的代码。这个函数只能在当前线程中调用,用于退出当前线程。

  1. 线程终止

线程终止有三种方法:
①线程函数return返回
②线程调用pthread_exit()
③同一线程组内线程调用pthread_cancle()终止指定线程 。

  1. 线程等待

线程终止后的有两种处理方式,
①对待使用pthread_detach()进行线程分离的线程,线程资源会被自动回收,其他线程不关心被分离线程的返回结果。
② 对待一般的线程终止,需要有其他线程调用prtread_jion函数对其进行处理。
因为一般线程终止后其在进程地址空间中所占用的线程库资源未被释放,需要pthread_jion函数去释放,且一般线程终止后的结果也需要pthread_join函数接收,对pthread_join函数接收结果分析,可得到线程终止的原因和运行结果。

注意:线程分离和等待是冲突的,线程被设置pthread_detach() 就不可以用pthread_jion函数去等待该进程结束。
在这里插入图片描述

  1. 主线程

主线程在进程启动时自动创建,并且在进程结束时自动退出。当主线程执行完所有的代码后,进程会自动终止,不需要显式地调用pthread_exit()函数或者其他线程等待主线程。 主线程也可以其他线程结束前手动结束。

主线程几乎没什么特别之处,线程和进程不一样,进程有父进程的概念,但在线程组里面,所有的线程都是对等关系。

补充与说明

  1. pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) 

是一个 POSIX 线程库中的函数,用于创建一个新的线程。

参数说明:

thread:指向 pthread_t 类型的指针,用于存储新线程的标识符。
attr:指向 pthread_attr_t 类型的指针,用于指定新线程的属性,可以为 NULL,表示使用默认属性。
start_routine:是一个函数指针,指向新线程要执行的函数。该函数的返回类型是 void *,接受一个 void * 类型的参数。
arg:是一个 void * 类型的参数,作为 start_routine 函数的参数传递给新线程。
返回值:

如果成功创建新线程,则返回 0。
如果发生错误,则返回一个非零的错误代码,表示创建线程失败。
pthread_create 函数的作用是在调用它的线程中创建一个新的线程。新线程会立即开始执行 start_routine 函数,并使用 arg 作为参数传递给 start_routine 函数。线程的创建是异步的,即 pthread_create 函数会立即返回,不会等待新线程的结束。

#include <stdio.h>
#include <pthread.h>void* thread_func(void* arg) {int* value = (int*)arg;printf("Hello from thread! Value: %d\n", *value);pthread_exit(NULL);
}int main() {pthread_t thread;int value = 10;// 创建线程int result = pthread_create(&thread, NULL, thread_func, &value);if (result != 0) {printf("Failed to create thread.\n");return 1;}// 等待线程结束result = pthread_join(thread, NULL);if (result != 0) {printf("Failed to join thread.\n");return 1;}printf("Thread finished.\n");return 0;
}
  1. 错误检查:

传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
pthreads函(库)数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小

  1. pthread_exit

pthread_exit 函数是 POSIX 线程库中的一个函数,用于终止当前线程的执行并返回一个值。它的函数原型如下:

void pthread_exit(void *value_ptr);

该函数接受一个指向任意类型的指针 value_ptr,用于传递线程的返回值。线程的返回值可以通过其他线程使用 pthread_join 函数来获取。

当一个线程调用 pthread_exit 函数时,它会立即终止自身的执行,并将 value_ptr 指向的值作为线程的返回值。其他线程可以通过 pthread_join 函数来等待该线程的终止,并获取它的返回值。

需要注意的是,如果线程在调用 pthread_exit 函数之前没有调用 pthread_detach 函数将自己分离,那么它的资源(如栈空间)将不会被释放,从而可能导致资源泄漏。

另外,调用 pthread_exit 函数并不会终止整个进程,只会终止当前线程的执行。如果想要终止整个进程,可以使用 exit 函数。

  1. pthread_cancel

pthread_cancel 函数是 POSIX 线程库中的一个函数,用于请求取消指定线程的执行。它的函数原型如下:

int pthread_cancel(pthread_t thread);

该函数接受一个 pthread_t 类型的参数 thread,用于指定要取消的线程。

当调用 pthread_cancel 函数时,它会向指定的线程发送一个取消请求。被取消的线程会在某个取消点(cancellation point)处终止执行,并根据取消类型的设置进行相应的处理。

取消请求的处理方式取决于被取消线程的取消状态和取消类型的设置。取消状态有三种可能的取值:

PTHREAD_CANCEL_ENABLE:允许线程被取消。
PTHREAD_CANCEL_DISABLE:禁止线程被取消。
PTHREAD_CANCEL_DEFERRED:推迟取消请求,直到线程到达取消点。
取消类型有两种可能的取值:

PTHREAD_CANCEL_ASYNCHRONOUS:异步取消。取消请求立即生效,被取消线程无法进行清理操作。
PTHREAD_CANCEL_DEFERRED:推迟取消。取消请求推迟到线程到达取消点时生效,被取消线程有机会进行清理操作。
需要注意的是,pthread_cancel 函数只是向目标线程发送取消请求,并不能保证目标线程会立即终止执行。被取消的线程需要在代码中显式地检查取消请求,并在适当的时候调用 pthread_exit 函数来终止自身的执行

  1. thread_join

thread_join()函数是用于等待指定线程的结束,并获取线程的返回值。

它的参数包括:

thread:要等待的线程标识符(pthread_t类型),通常是通过调用pthread_create()创建线程时返回的标识符。
value_ptr:一个指向指针的指针,用于接收线程的返回值。线程的返回值是一个void*类型的指针,通过value_ptr传递给调用者。
pthread_join()函数会阻塞调用它的线程,直到指定的线程结束。一旦指定的线程结束,pthread_join()函数会返回,并且可以通过value_ptr获取线程的返回值。

需要注意的是,如果不关心线程的返回值,可以将value_ptr参数设置为NULL。

pthread_join()函数的返回值为0表示成功,非0值表示失败。

使用pthread_join()函数可以确保在主线程中等待其他线程的结束,以免主线程提前退出导致其他线程无法完成任务。

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

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

相关文章

【电子通识】开关的种类

开关在我们日常生活与工作中使用较多。开关有无数种形式&#xff0c;种类繁多。从微小的按钮到巨大的控制器&#xff0c;功能多种多样。这种多样性受到机械或电气操作、手动或电子控制等因素的影响&#xff0c;并且与个人在设计美学和用户界面方面的偏好也有关。 电子开关采用 …

LabVIEW利用视觉引导机开发器人精准抓取

LabVIEW利用视觉引导机开发器人精准抓取 本项目利用单目视觉技术指导多关节机器人精确抓取三维物体的技术。通过改进传统的相机标定方法&#xff0c;结合LabVIEW平台的Vision Development和Vision Builder forAutomated Inspection组件&#xff0c;优化了摄像系统的标定过程&a…

低代码平台在金融银行中的应用场景

随着数字化转型的推进&#xff0c;商业银行越来越重视技术在业务发展中的作用。在这个背景下&#xff0c;白码低代码平台作为一种新型的开发方式&#xff0c;正逐渐受到广大商业银行的关注和应用。白码低代码平台能够快速构建各类应用程序&#xff0c;提高开发效率&#xff0c;…

概率论相关题型

文章目录 概率论的基本概念放杯子问题条件概率与重要公式的结合独立的运用 随机变量以及分布离散随机变量的分布函数特点连续随机变量的分布函数在某一点的值为0正态分布标准化随机变量函数的分布 多维随机变量以及分布条件概率max 与 min 函数的相关计算二维随机变量二维随机变…

<JavaEE> TCP 的通信机制(五) -- 延时应答、捎带应答、面向字节流

目录 TCP的通信机制的核心特性 七、延时应答 1&#xff09;什么是延时应答&#xff1f; 2&#xff09;延时应答的作用 八、捎带应答 1&#xff09;什么是捎带应答&#xff1f; 2&#xff09;捎带应答的作用 九、面向字节流 1&#xff09;沾包问题 2&#xff09;“沾包…

NXP实战笔记(三):S32K3xx基于RTD-SDK在S32DS上配置WDT配置

目录 1、WDT概述 2、SWT配置 2.1、超时时间&#xff0c;复位方式的配置 2.2、中断形式 1、WDT概述 SWT 编程模型只允许 32 位&#xff08;字&#xff09;访问。 以下任何尝试访问都是无效的: •非32位访问 •写入只读寄存器 •启用SWT时&#xff0c;将不正确的值写入SR…

Mongodb基础介绍与应用场景

NoSql 解决方案第二种 Mongodb MongoDB 是一款开源 高性能 无模式的文档型数据库 当然 它是NoSql数据库中的一种 是最像关系型数据库的 非关系型数据库 首先 最需要注意的是 无模式的文档型数据库 这个需要后面我们看到它的数据才能明白 其次是 最像关系型数据库的非关系型数据…

基于采样的自动驾驶规划算法 - PRM,RRT,RRT*,CL-RRT

本文将讲解PRM&#xff0c;RRT&#xff0c;RRT*自动驾驶规划算法原理&#xff0c;不正之处望读者指正 0 前言 机器人运动规划的基本任务&#xff1a;从开始位置到目标位置的运动 &#xff08;1&#xff09;如何躲避构型空间出现的障碍物 &#xff08;2&#xff09;如何满足机器…

小型企业成为网络犯罪分子获取数据的目标

在过去十年的大部分时间里&#xff0c;网络犯罪的巨额资金来自针对大型组织的勒索软件攻击。这种威胁仍然存在。但犯罪分子可能会将注意力转向中小企业 (SMB)。这对消费者的影响将是巨大的。 将软件即服务 (SaaS) 技术用于核心业务功能继续将中小企业整合到全球供应链中。由于…

【C语言】数据结构——排序(一)

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;数组打印与交换1. 插入排序1.1 直接插入排序1.1.1 基本思想1.1.2 实现代码1.1.3 图解 1.2 希尔排序1.2.1…

C语言之进制转换

C语言之进制转换 一、引言二、十进制与二进制、八进制、十六进制三、二进制与八进制、十六进制四、八进制与十六进制 一、引言 在C语言中&#xff0c;经常使用的整数的进制有十进制、二进制、十六进制&#xff08;在C语言中以0x或0X为前缀&#xff09;、八进制&#xff08;在C…

如何手动升级Chrome插件/Chrome扩展程序?

Chrome 浏览器的插件&#xff08;也称为扩展&#xff09;通常会自动更新到最新版本。这是因为 Chrome 会定期检查并下载来自 Chrome 网上应用店的扩展更新。然而&#xff0c;如果你需要手动更新扩展&#xff0c;可以按照以下步骤操作&#xff1a; 打开 Chrome 浏览器。点击浏览…

Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】

在Three.js基础入门介绍——Three.js学习二【极简入门】中介绍了如何搭建Three.js开发环境并实现一个包含旋转立方体的场景示例&#xff0c;以此为前提&#xff0c;本篇将引进一个控制器的概念并使用”轨道控制器”&#xff08;OrbitControls&#xff09;来达到从不同方向展示场…

Flink项目实战篇 基于Flink的城市交通监控平台(上)

系列文章目录 Flink项目实战篇 基于Flink的城市交通监控平台&#xff08;上&#xff09; Flink项目实战篇 基于Flink的城市交通监控平台&#xff08;下&#xff09; 文章目录 系列文章目录1. 项目整体介绍1.1 项目架构1.2 项目数据流1.3 项目主要模块 2. 项目数据字典2.1 卡口…

车路协同中 CUDA 鱼眼相机矫正、检测、追踪

在车路协同中,鱼眼一般用来补充杆件下方的盲区,需要实现目标检测、追踪、定位。在目标追踪任务中,通常的球机或者枪机方案,无法避免人群遮挡的问题,从而导致较高的ID Swich,造成追踪不稳定。但是鱼眼相机的顶视角安装方式,天然缓解了遮挡的问题,从而实现杆件下方的盲区…

51单片机(STC8)-- GPIO输入输出

文章目录 I/O口相关寄存器端口数据寄存器端口模式配置寄存器&#xff08;PxM0&#xff0c;PxM1&#xff09;端口上拉电阻控制寄存器(PxPU)关于I/O的注意事项 配置I/O口I/O设置demoI/O端口模式LED控制&#xff08;I/O输出&#xff09;按键检测&#xff08;I/O输入&#xff09; S…

SpringBoot多线程与任务调度总结

一、前言 多线程与任务调度是java开发中必须掌握的技能&#xff0c;在springBoot的开发中&#xff0c;多线程和任务调度变得越来越简单。实现方式可以通过实现ApplicationRunner接口&#xff0c;重新run的方法实现多线程。任务调度则可以使用Scheduled注解 二、使用示例 Slf…

vue3中使用defineComponent封装hook实现模板复用

文章目录 一、前言二、useTemplate 实现三、最后 一、前言 最近在做 Vue3 项目的时候&#xff0c;在思考一个小问题&#xff0c;其实是每个人都做过的一个场景&#xff0c;很简单&#xff0c;看下方代码 <template><div><div v-for"item in data" :…

Eclipse安装Jrebel eclipse免重启加载项目

每次修改JAVA文件都需要重新启动项目&#xff0c;加载时间太长&#xff0c;eclipse安装jrebel控件,避免重启项目节省时间。 1、Help->Eclipse Marketplace 2、搜索jrebel 3、Help->jrebel->Configuration 配置jrebel 4、激活jrebel 5、在红色框中填入 http://jrebel…

HCIA-Datacom题库(自己整理分类的)——OSPF协议判断

1.路由表中某条路由信息的Proto为OSPF则此路由的优先级一定为10。√ 2.如果网络管理员没有配置骨干区域,则路由器会自动创建骨干区域&#xff1f; 路由表中某条路由信息的Proto为OSPF&#xff0c;则此路由的优先级一定为10。 当两台OSPF路由器形成2-WAY邻居关系时&#xff0…