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.mzph.cn/news/583084.shtml

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

相关文章

flutter 安卓使用高德插件黑屏

地址 https://lbs.amap.com/api/android-sdk/guide/create-project/android-studio-create-project 下面介绍的方式是Native配置 sdk&#xff0c;也就是需要手动下载到本地在引入的方式 1、添加 jar 文件&#xff1a; 将下载的地图 SDK 的 jar包复制到工程&#xff08;此处截…

2702 高级打字机

因为Undo操作只能撤销Type操作&#xff0c;所以Undo x 实际上就是删除文章末尾x个字母。用一个栈即可解决&#xff08;每个字母最多进出一次&#xff09;。 这种情况下只需要设计一个合理的数据结构依次执行操作即可。 版本树&#xff1a;Undo x撤销最近的x次修改操作&#xf…

将正规文法转化为正规式

将正规文法转化为正规式有以下几个规则&#xff1a; 通过一道例题来讲解&#xff1a; ①A-->aC|bA ②C-->bD ③D-->aC|bD| (1)首先将②带入③&#xff08;不能将自身带入自身例如D-->aC|bD|,文法中带D&#xff0c;不能带入D&#xff09; DabD|bD|&#xff08;…

ARM CCA机密计算软件架构之内存加密上下文(MEC)

内存加密上下文(MEC) 内存加密上下文是与内存区域相关联的加密配置,由MMU分配。 MEC是Arm Realm Management Extension(RME)的扩展。RME系统架构要求对Realm、Secure和Root PAS进行加密。用于每个PAS的加密密钥、调整或加密上下文在该PAS内是全局的。例如,对于Realm PA…

公司创建百度百科需要哪些内容?

一个公司或是一个品牌想要让自己更有身份&#xff0c;更有知名度&#xff0c;更有含金量&#xff0c;百度百科词条是必不可少的。通过百度百科展示公司的详细信息&#xff0c;有助于增强用户对公司的信任感&#xff0c;提高企业形象。通过百度百科展示公司的发展历程、领导团队…

AI-ChatGPTCopilot

ChatGPT chatGPT免费网站列表&#xff1a;GitHub - LiLittleCat/awesome-free-chatgpt: &#x1f193;免费的 ChatGPT 镜像网站列表&#xff0c;持续更新。List of free ChatGPT mirror sites, continuously updated. Copilot 智能生成代码工具 安装步骤 - 登录 github&am…

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…

uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新

实现登录即更新&#xff0c;或实时监听更新 本文介绍的是在App打开启动的时候调用更新&#xff0c;点击下方链接&#xff0c;查看使用WebSocket实现实时通知在线用户更新。 uniapp&#xff1a;全局消息是推送&#xff0c;实现app在线更新&#xff0c;WebSocket&#xff0c;ap…

java 纯代码导出pdf合并单元格

java 纯代码导出pdf合并单元格 接上篇博客 java导出pdf&#xff08;纯代码实现&#xff09; 后有一部分猿友叫我提供一下源码&#xff0c;实际上我的源码已经贴在帖子上了&#xff0c;都是同样的步骤&#xff0c;只是加多一点设置就可以了。今天我再次上传一下相对情况比较完整…

开源预约挂号平台 - 从0到上线

文章目录 开源预约挂号平台 - 从0到上线演示地址源码地址可以学到的技术前端技术后端技术部署上线开发工具其他技术业务功能 项目讲解前端创建项目 - 安装PNPM - 使用VSCODE - 安装插件首页顶部与底部 - 封装组建 - 使用scss左右布局中间内容部分路由 - vue-routerBANNER- 走马…

重装系统以后无法git跟踪

总结&#xff1a;权限问题 故障定位 解决方案&#xff1a; 复制一份新的文件夹。&#xff08;新建的文件创建和写入权限都变了&#xff09; 修改文件为新的用户 执行提示的命令

SuperMap iServer发布的ArcGIS REST 地图服务如何通过ArcGIS API进行要素查询

作者&#xff1a;yx 前言 前面我们介绍了SuperMap iServer发布的ArcGIS REST 地图服务如何通过ArcGIS API加载&#xff0c;这里呢我们再来看看如何进行要素查询呢&#xff1f; 一、服务发布 SuperMap iServer发布的ArcGIS REST 地图服务如何通过ArcGIS API加载已经介绍如何发…

日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

前言 随着互联网和大数据的迅猛发展&#xff0c;分布式日志系统和日志分析系统已广泛应用&#xff0c;几乎所有应用程序都使用各种日志框架记录程序运行信息。因此&#xff0c;作为工程师&#xff0c;了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影…

OpenEular23.09(欧拉)操作系统为企业搭建独立的K8S集群环境,详细流程+截图

1.环境&#xff1b; win10&#xff0c;vmware16 pro&#xff0c;openeular23.09 集群模式&#xff1a;一主二从 主机硬件配置 主机名IP角色CPU内存硬盘k8s-master01192.168.91.100master4C4G40Gk8s-worker02192.168.91.101worker(node)4C4G40Gk8s-worker03192.168.91.102work…

详解全志R128 RTOS安全方案功能

介绍 R128 下安全方案的功能。安全完整的方案基于标准方案扩展&#xff0c;覆盖硬件安全、硬件加解密引擎、安全启动、安全系统、安全存储等方面。 配置文件相关 本文涉及到一些配置文件&#xff0c;在此进行说明。 env*.cfg配置文件路径&#xff1a; board/<chip>/&…

字符串转成时间的SQL,一个多种数据库通用的函数

select date 2010-10-06 from dual; date 函数&#xff0c;此函数适用于&#xff1a; 1.MySQL数据库 2.Oracle数据库 3.达梦数据库 4.人大金仓数据库

linux用户态与内核态通过字符设备交互

linux用户态与内核态通过字符设备交互 简述 Linux设备分为三类&#xff0c;字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取&#xff0c;常见外设基本都是字符设备。块设备一般用于存储设备&#xff0c;一块一块的读取。网络设备&#xff0c;Linux将对网络…

20231228在Firefly的AIO-3399J开发板的Android11使用Firefly的DTS配置单前后摄像头ov13850

20231228在Firefly的AIO-3399J开发板的Android11使用Firefly的DTS配置单前后摄像头ov13850 2023/12/28 19:20 缘起&#xff0c;突然发现只能打开前置的ov13850&#xff0c;或者后置的ov13850。 但是不能切换&#xff01; 【SDK&#xff1a;rk3399-android-11-r20211216.tar.xz】…

Windows搭建RTSP视频流服务(EasyDarWin服务器版)

文章目录 引言1、安装FFmpeg2、安装EasyDarWin3、实现本地\虚拟摄像头推流服务4、使用VLC或PotPlayer可视化播放器播放视频5、RTSP / RTMP系列文章 引言 RTSP和RTMP视频流的区别 RTSP &#xff08;Real-Time Streaming Protocol&#xff09;实时流媒体协议。 RTSP定义流格式&am…

[BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务

1.问题描述 使用yarn调度任务时&#xff0c;在CapacityScheduler页面上单击叶队列&#xff08;或子队列&#xff09;时&#xff0c;不会显示应用程序任务信息&#xff0c;root队列可以显示任务。此外&#xff0c;FairScheduler页面是正常的。 No matching records found2.原…