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.hqwc.cn/news/305147.html

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

相关文章

K8s实战-init容器

概念&#xff1a; 初始化容器的概念 比如一个容器A依赖其他容器&#xff0c;可以为A设置多个 依赖容易A1&#xff0c;A2&#xff0c;A3 A1,A2,A3要按照顺序启动&#xff0c;A1没有启动启动起来的 话&#xff0c;A2,A3是不会启动的&#xff0c;直到所有的静态容器全 部启动完毕…

编译错误:C4056E type of input file ‘xxx‘ unknown

最近在Proteus上面进行仿真&#xff0c;将编译后的hex文件导入到电路图中&#xff0c;进行程序运行的时候&#xff0c;Proteus报了这么一个错误&#xff1a;Error: C4065E: type of input file Pian unknown 我上网一搜&#xff0c;好像与文件名称中存在空格有关&#xff0c;导…

Mybatis枚举类型处理和类型处理器

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 文章目录 专栏精选摘要引言正文枚举类型映射简单枚举映射枚举顺序映射复杂枚举映射 类型处理器 总结 摘要 在这篇…

CNN实现对手写字体的迭代

导入库 import torchvision import torch from torchvision.transforms import ToTensor from torch import nn import matplotlib.pyplot as plt 导入手写字体数据 train_dstorchvision.datasets.MNIST(data/,trainTrue,transformToTensor(),downloadTrue) test_dstorchvis…

CSDN被关注自动回复消息怎么设置

CSDN被关注自动回复消息怎么设置 1.鼠标移动到头像 2.点击内容管理 3.拉到下面&#xff0c;看到工具目录&#xff0c;里面有个粉丝服务点击 4.点击自动回复 5.这里点击打开&#xff0c;选择被关注自动回复&#xff0c;设置文字或图片点击保存就好了

SDRAM小项目——uart发送模块

数据的输入&#xff1a; 数据的输入是uart接收模块的输出&#xff1a;串—并—串&#xff08;接收到的外部的串行数据进入内部寄存器转化为并行数据&#xff0c;再由内部寄存器读出&#xff0c;输出表现为串行数据&#xff09;&#xff0c;接收到的输入信号为tx_flag&#xff0…

【机组期末速成】计算机的运算方法|进制转换|无符号数与有符号数|数的定点表示与浮点表示|定点运算

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;计算机组成原理&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言&#xff1a; 一、本章考点总览 二、考点分析 1、日常我们采用十进制来表示数据&#xff0c;计算机如何表示&#xf…

24、Qt使用QCustomPlot

一、下载文件 进入官网&#xff0c;选择“Download”、QCustomPlot.tar.gz Qt Plotting Widget QCustomPlot - Download 二、创建项目 创建一个"Qt Widget Application"项目&#xff0c;基类选择“QMainWindow”&#xff0c;把刚才下载的压缩包里的“qcustomplot.…

K8S网络类型

k8s的网络类型 k8s的通信模式 1 pod内部之间容器与容器之间的通信&#xff0c;在同一个pod中容器是共享资源和网络&#xff0c;使用同一个网络命名空间&#xff0c;可以直接通信 2 同一个node节点之内&#xff0c;不同pod之间的通信&#xff0c;每个pod都有一个全局的真实ip地…

美国Top科技公司年薪大曝光,OpenAI 600万高居榜首!

全美顶尖AI公司年薪大曝光&#xff01; OpenAI 600万高居榜首&#xff0c;微软、英伟达只有OpenAI 的一半。 近日&#xff0c;美国一家帮助博士生协商薪资的公司Rora发布了一份薪资报告&#xff0c;公布了这些顶尖AI公司给研究人员开出的平均薪水。 以下是部分顶级AI公司的名…

数据结构和算法-B树(B树的查找 B树的最大高度和最小高度)

文章目录 回顾&#xff1a;二叉查找树五叉查找树如何查找查找成功查找失败如何保证查找效率 B树B树的高度小结 回顾&#xff1a;二叉查找树 五叉查找树 进一步对范围划分&#xff0c;处于不同划分进入不同子树 四个数做划分&#xff0c;此时有五个区间 此时一个节点对应多个关…

[RoarCTF2019] TankGame

不多说&#xff0c;用dnspy反编译data文件夹中的Assembly-CSharp文件 使用分析器分析一下可疑的FlagText 发现其在WinGame中被调用&#xff0c;跟进WinGame函数 public static void WinGame(){if (!MapManager.winGame && (MapManager.nDestroyNum 4 || MapManager.n…