Linux 线程概念

news2024/11/28 20:46:39

文章目录

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

前言

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

线程的概念

网络回答: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;
    }
    
    // 获取本线程的线程ID
    pthread_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.coloradmin.cn/o/1341353.html

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

相关文章

易趋产品升级(EasyTrack 11_V1.3) | 集成飞书、WPS、个性化设置,增强团队协作和用户体验

企业在项目管理过程中&#xff0c;经常会遇到项目信息同步不及时、沟通障碍以及管理软件使用不便捷等难题&#xff0c;导致团队协作效率低下。这种情况下&#xff0c;如果使用了多个办公软件&#xff08;如&#xff1a;钉钉、企业微信、项目管理软件等&#xff09;&#xff0c;…

【DC-DC】这是一款半亮 高亮 瀑闪的LED降压恒流刹车灯线路图

1产品描述 AP2402 是一款 PWM 工作模式,高效率、外围简单、内置功率管&#xff0c;适用于 5-100V输入的高精度降压 LED 恒流驱动芯片。输出功率可达 15W&#xff0c;电流 1.5A。AP2402 可实现三段功能切换&#xff0c;通过MODE1/2/3 切换三种功能模式&#xff1a;全亮&#xf…

STM32 学习(一)新建工程

本课程使用的stm32型号 引脚定义&#xff0c;有FT能接5v&#xff0c;没有FT能接3.3v 启动配置 第二种启动模式中&#xff0c;系统存储器中存放了一部分Bootloader程序&#xff0c;该程序可以接收串口的数据&#xff0c;然后刷新到主闪存中&#xff0c;这样就可以使用串口下载程…

【经验分享】日常开发中的故障排查经验分享(一)

目录 简介CPU飙高问题1、使用JVM命令排查CPU飙升100%问题2、使用Arthas的方式定位CPU飙升问题3、Java项目导致CPU飙升的原因有哪些&#xff1f;如何解决&#xff1f; OOM问题&#xff08;内存溢出&#xff09;1、如何定位OOM问题&#xff1f;2、OOM问题产生原因 死锁问题的定位…

uni-app引入vant表单(附源码)

新建项目 下载安装vant npm i vant main.js引入 import { Form } from vant; import { Field } from vant;Vue.use(Form); Vue.use(Field);代码引入 <van-form submit"onSubmit"><van-fieldclass"rePwd"v-model"username"name"请…

【51单片机系列】DS18B20温度传感器扩展实验之设计一个智能温控系统

本文是关于DS18B20温度传感器的一个扩展实验。 文章目录 一、相关元件介绍二、实验分析三、proteus原理图设计四、软件设计 本扩展实验实现的功能&#xff1a;利用DS18B20设计一个智能温度控制系统&#xff0c;具有温度上下限值设定。当温度高于上限值时&#xff0c;电机开启&a…

flask之文件管理系统-项目 JRP上线啦!!! ----Bug版

既然单人应用那么就不需要注册、登录了&#xff0c;太麻烦&#xff0c;直接上功能项&#xff0c;而初版太不好看了&#xff0c;略微修改归纳了一下&#xff0c;出了第一版 很有精神&#xff0c;为了纪念&#xff0c;这个网页项目我命名为JRP 主要就是&#xff1a; 1、定义一个类…

Kubernetes之Ingress详解

目录 IngressIngress配置配置写法转发到单个后端服务不同的URL路径被转发到不同的服务上不同的域名转发到不同的服务上不使用域名的转发规则 Ingress Ingress 是 Kubernetes 中的一种 API 对象&#xff0c;用于管理和配置集群中的 HTTP 和 HTTPS 服务路由。 Ingress 可以在 K…

目标检测-One Stage-YOLOv1

文章目录 前言一、YOLOv1的网络结构和流程二、YOLOv1的损失函数三、YOLOv1的创新点总结 前言 前文目标检测-Two Stage-Mask RCNN提到了Two Stage算法的局限性&#xff1a; 速度上并不能满足实时的要求 因此出现了新的One Stage算法簇&#xff0c;YOLOv1是目标检测中One Stag…

如何批量提取pdf文件名到excel?

如何批量提取pdf文件名到excel&#xff1f;在大家整理PDF文档的时候会不会遇到下面这些问题&#xff0c;首先PDF过多&#xff0c;每个PDF文件都有自己的名字&#xff0c;我们想要分类排放的话非常麻烦&#xff0c;不仅耗费时间而且带来的收益非常低&#xff0c;然后即使我们整理…

docker +gitee+ jenkins +maven项目 (一)

jenkins环境和插件配置 文章目录 jenkins环境和插件配置前言一、环境版本二、jenkins插件三、环境安装总结 前言 现在基本都是走自动化运维&#xff0c;想到用docker 来部署jenkins &#xff0c;然后jenkins来部署java代码&#xff0c;做到了开箱即用&#xff0c;自动发布代码…

eBay自养号测评:提升销量与排名的安全可控之道

近年来&#xff0c;eBay平台吸引了大量商家入驻&#xff0c;许多原本在其他平台的卖家也纷纷转型至eBay。然而&#xff0c;许多商家在运营一段时间后发现&#xff0c;新账号的流量扶持期结束后&#xff0c;店铺流量开始下滑。面对这种情况&#xff0c;卖家应该采取哪些措施呢&a…

elasticsearch系列五:集群的备份与恢复

概述 前几篇咱们讲了es的语法、存储的优化、常规运维等等&#xff0c;今天咱们看下如何备份数据和恢复数据。 在传统的关系型数据库中我们有多种备份方式&#xff0c;常见有热备、冷备、全量定时增量备份、通过开发程序备份等等&#xff0c;其实在es中是一样的。 官方建议采用s…

YOLOv5改进 | 2023主干篇 | 华为最新VanillaNet主干替换Backbone实现大幅度长点

一、本文介绍 本文给大家来的改进机制是华为最新VanillaNet网络&#xff0c;其是今年最新推出的主干网络&#xff0c;VanillaNet是一种注重极简主义和效率的神经网络架构。它的设计简单&#xff0c;层数较少&#xff0c;避免了像深度架构和自注意力这样的复杂操作(需要注意的是…

用html,js和layui写一个简单的点击打怪小游戏

介绍&#xff1a; 一个简单的打怪小游戏&#xff0c;点击开始游戏后&#xff0c;出现攻击按钮&#xff0c;击败怪物后可以选择继续下一关和结束游戏。 继续下一个怪兽的血量会增加5点&#xff0c;攻击按钮会随机变色。 效果图&#xff1a; html代码&#xff1a; <!DOCTYPE…

2702 高级打字机

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

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

ospf的hello报文功能是 邻居发现 同步路由器的LSDB 更新LSA信息 维持邻居关系 下列关于OSPF区域描述正确的是 在配置OSPF区域正确必须给路由器的loopback接配置IP地址 所有的网络都应在区域0中宣告 骨干区域的编号不能为2 区域的编号范围是从0.0.0.0到255.255.255.255…

《深入理解Java虚拟机(第三版)》读书笔记:Java内存区域与内存溢出异常、垃圾收集器与内存分配策略

下文是阅读《深入理解Java虚拟机&#xff08;第3版&#xff09;》这本书的读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 第2章 Java内存区域与内存溢出异常2.2 运行时数据区域2.3 HotSpot虚拟机对象探秘 第3章 垃圾收集器与内存分配策略3.2 对象已死&…

应用在网络摄像机领域中的国产音频ADC芯片

IPC&#xff1a;其实叫“网络摄像机”&#xff0c;是IP Camera的简称。它是在前一代模拟摄像机的基础上&#xff0c;集成了编码模块后的摄像机。它和模拟摄像机的区别&#xff0c;就是在新增的“编码模块”上。模拟摄像机&#xff0c;顾名思义&#xff0c;输出的是模拟视频信号…

Adobe Premier及PrElements各版本安装指南

下载链接 https://pan.baidu.com/s/1FI_Zk4OsyRtx8AiMzgU57w?pwd0531 #2024版 1.鼠标右击【Pr2024(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Pr2024(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Setup】选择【以…