Linux 线程概念

一. 线程的基本概念

线程是进程内的一个执行单元,它是调度和执行的基本单位。

1.1 Linux中的线程

在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。
在这里插入图片描述

1.2 线程的优点

  1. 创建一个新线程的代价要比创建一个新进程小得多。
  2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
  3. 线程占用的资源要比进程少很多。
  4. 能充分利用多处理器的可并行数量。
  5. 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
  6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  7. IO密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

1.3 线程的缺点

  1. 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失。
  2. 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性很大,线程之间是缺乏保护的。
  3. 进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
  4. 编写与调试一个多线程程序比单线程程序困难得多。

1.4 线程异常

线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。

二. 进程 VS 线程

2.1 进程和线程之间的特点

  • 进程是资源分配的基本单位。

  • 线程是调度的基本单位。

  • 线程共享进程数据:

    1. 文件描述符表
    2. 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
    3. 当前工作目录
    4. 用户id和组id
    5. 全局变量和静态变量
    6. 进程地址空间
  • 线程独有的数据:

    1. 线程ID
    2. 一组寄存器
    3. errno
    4. 信号屏蔽字
    5. 调度优先级

2.2 进程和线程之间的关系

在这里插入图片描述

三. POSIX线程库

3.1 POSIX线程库的介绍

  1. 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头。

  2. 要使用这些函数库,要通过引入头文<pthread.h>。

  3. 链接这些线程函数库时要使用编译器命令的“-lpthread”选项。

    g++ -o test test.cpp -lpthread
    

3.2 线程ID以及进程地址空间布局

  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。

  • 线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。

  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,

  • 属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。

  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:

    pthread_t pthread_self(void);
    

pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址:
在这里插入图片描述

3.2 线程创建

pthread_create:

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

参数说明:

  • pthread_t *thread:指向一个类型为 pthread_t 的指针,用来存储新创建线程的标识符。如果线程成功创建,那么这个指针指向的变量会被设置成新线程的ID。
  • const pthread_attr_t *attr:指向一个线程属性对象的指针,该对象包含了线程创建时的一些特殊属性设置。如果不需要特殊的属性设置,可以传入 NULL 使用默认属性。
  • void (start_routine)(void):这是新线程启动后将要执行的函数地址。该函数应该接受一个 void 类型的参数,并返回一个 void* 类型的结果。
  • void arg:这是一个传递给 start_routine 函数的参数,可以是任何类型的数据,但必须转换为 void 类型。

返回值:

  • 如果线程创建成功,pthread_create() 函数返回 0。
  • 如果失败,则返回一个非零的错误码。常见的错误码包括:
    EAGAIN:系统资源不足,无法创建新的线程。
    EINVAL:输入的属性对象无效。
    EPERM:调用者没有权限创建线程。

例子:

#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>// 新线程执行的函数
void* print_message_function(void* ptr) {char *message;message = (char *) ptr;printf("%s Begin!\n", message);sleep(3); // 模拟耗时操作printf("%s End!\n", message);pthread_exit(NULL);
}int main() {pthread_t thread1, thread2;const char *message1 = "This is Thread 1";const char *message2 = "This is Thread 2";// 创建线程int ret1 = pthread_create(&thread1, NULL, print_message_function, (void*)message1);if (ret1) {fprintf(stderr, "Error - pthread_create() return code: %d\n", ret1);exit(EXIT_FAILURE);}int ret2 = pthread_create(&thread2, NULL, print_message_function, (void*)message2);if (ret2) {fprintf(stderr, "Error - pthread_create() return code: %d\n", ret2);exit(EXIT_FAILURE);}// 等待两个线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);printf("Threads completed successfully.\n");return 0;
}

在这里插入图片描述

3.3 线程终止

pthread_exit:

void pthread_exit(void *value_ptr);

参数说明:

  • void *value_ptr:一个指向任意类型的指针,用于向等待该线程结束的其他线程传递退出状态或结果。通常情况下,如果线程正常结束,可以传递 NULL 或者线程计算出的结果。

例子:

#include <cstdio>
#include <pthread.h>void* thread_function(void *arg) {printf("Thread is running...\n");pthread_exit((void*)123); // 退出线程并返回值 123
}int main() {pthread_t thread;void *result;pthread_create(&thread, NULL, thread_function, NULL);pthread_join(thread, &result);printf("Thread returned value: %ld\n", (long)result); // 输出线程返回的值return 0;
}

在这里插入图片描述

pthread_cancel:

int pthread_cancel(pthread_t thread);

参数说明:

  • pthread_t thread:要取消的线程的 ID。

例子:

#include <cstdio>
#include <pthread.h>
#include <unistd.h>void* thread_function(void *arg) {while (1) {printf("Thread is running...\n");sleep(1);}return NULL;
}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_function, NULL);sleep(3); // 让主线程等待一段时间pthread_cancel(thread); // 请求取消线程pthread_join(thread, NULL); // 等待线程结束printf("Thread has been canceled.\n");return 0;
}

在这里插入图片描述

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

3.4 线程等待

pthread_join:

int pthread_join(pthread_t thread, void **value_ptr);

参数说明:

  • pthread_t thread:要等待的线程的 ID。
  • void **value_ptr:一个指向指针的指针,用于接收线程的退出状态或结果。如果不需要接收退出状态,可以传入 NULL。

返回值:

  • 如果成功,pthread_join() 函数返回 0。
  • 如果失败,返回一个非零的错误码。常见的错误码包括:
    EINVAL:指定的线程 ID 无效。
    ESRCH:指定的线程不存在。
    EDEADLK:可能会发生死锁(例如,一个线程尝试等待自己)。

例子:

#include <cstdio>
#include <pthread.h>
#include <unistd.h>// 新线程执行的函数
void* thread_function(void* arg) {printf("Thread is running...\n");pthread_exit((void*)123);
}int main() {pthread_t thread;void *result;pthread_create(&thread, NULL, thread_function, NULL);// 等待线程结束pthread_join(thread, &result);// 输出线程返回的值printf("Thread returned value: %ld\n", (long)result);return 0;
}

在这里插入图片描述

线程为什么需要等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

为什么value_ptr是二级指针?

  • 如果 value_ptr 是一级指针(即 void *value_ptr),那么 pthread_join() 只能修改传入的指针本身,而不能修改指针所指向的内存。
  • 使用二级指针可以让 pthread_join() 修改指针所指向的内存,从而将线程的退出状态或结果赋值给调用者提供的变量。

3.5 分离线程

pthread_detach:

int pthread_detach(pthread_t thread);

参数说明:

  • pthread_t thread:要分离的线程的 ID。

返回值:

  • 如果成功,pthread_detach() 函数返回 0。
  • 如果失败,返回一个非零的错误码。常见的错误码包括:
    EINVAL:指定的线程 ID 无效。
    ESRCH:指定的线程不存在。
    EINVAL:线程已经是分离状态。

例子:

#include <cstdio>
#include <pthread.h>
#include <unistd.h>// 新线程执行的函数
void* thread_function(void* arg) {printf("Thread is running...\n");sleep(1); // 模拟耗时操作printf("Thread is about to exit...\n");pthread_exit(NULL); // 退出线程
}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_function, NULL);// 将线程标记为分离状态pthread_detach(thread);// 主线程继续执行printf("Main thread continues...\n");sleep(3); // 给新线程一些时间来运行printf("Main thread exiting...\n");return 0;
}

在这里插入图片描述

为什么会有分离线程?

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

————————————————————
感谢大家观看,不妨点赞支持一下吧喵~
如有错误,随时纠正喵~

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

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

相关文章

Qt聊天室项目

目录 项目要求 项目背景 技术分析 架构设计 服务器架构 模块划分 模块之间的交互 客户端架构 模块划分 模块之间交互 项目展示 项目实现 服务器 ui server.pro dialog.h dialog.cpp 客户端 ui cient.pro dialog.h dialog.cpp 打包步骤不做演示 视频演示 项目…

node.js模块化分析

什么是Node.js模块化 Node.js中的模块化‌是指将一个大文件拆分成独立且相互依赖的多个小模块。每个JS文件被视为一个独立的模块&#xff0c;模块之间是互相不可见的。如果一个模块需要使用另一个模块&#xff0c;则需要使用指定的语法来引入该模块&#xff0c;并且只能使用模块…

sql练习专场(一) (1-5)

这是总结的一些sql题目&#xff0c;共25道题&#xff0c;每个博客会写5道题 第一题 这道题需要找出连续活跃3天以上的用户&#xff0c;其中每个用户每天可以连续登录多次。 create table sql1_1(uid string,dt string );insert into sql1_1 values(A,2023-10-01),(A,2023…

自动化立体仓库:详细设计方案

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 这份文件是关于自动化立体仓库设计方案的详细介绍&#xff0c;包括了自动化立体仓库的重要性、特点、设计程序、机械部分设计方案、系统硬件设计、系统软件设计以及系统调试等关键部分…

windows运行ffmpeg的脚本报错:av_ts2str、av_ts2timestr、av_err2str => E0029 C4576

问题描述 我目前的环境是&#xff1a; 编辑器&#xff1a; Microsoft Visual Studio Community 2022 (64 位) 运行的脚本是ffmpeg自带的remux样例&#xff0c;只不过我想用c语言执行这个样例。在执行的过程中报错如下图&#xff1a; C4576 后跟初始值设定项列表的带圆括…

CentOS 文件系统扩容与缩容

一、 概述 理解Linux文件系统的管理&#xff0c;需要了解以下的一张图&#xff1a; 一般使用LVM (Logical Volume Manager) 管理磁盘存储&#xff0c;该工具允许用户更灵活地分配和管理存储空间。主要有以下几个概念&#xff1a; PV&#xff08;Physical Volume&#xff0c;物…

Linux系统使用第三方邮件客户端发送邮件

文章目录 安装第三方邮件客户端&#xff08;s-nail&#xff09;S-nail的简单介绍重要的特性差异 配置邮件服务配置文件 (以QQ邮箱为例)获取QQ邮箱授权码获取QQ服务器证书使用 OpenSSL 获取 QQ 邮箱服务器的证书安装OpenSSL连接到 QQ 邮箱的 SMTP 服务器并下载证书保存证书验证证…

家常菜点餐|基于java和小程序的家庭大厨家常菜点餐系统设计与实现(源码+数据库+文档)

家常菜点餐系统 目录 基于java和小程序的家庭大厨家常菜系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&am…

利士策分享,青年暴富难守,因何在?

利士策分享&#xff0c;青年暴富难守&#xff0c;因何在? 在人生的长河中&#xff0c;有些人似乎被命运特别眷顾&#xff0c;在年轻之时便轻易地获得了财富。 然而&#xff0c;令人遗憾的是&#xff0c;这些早年得志、财富易得的人&#xff0c;往往难以长久地守住这份来之不…

Echarts环形图引线设置

直接上图吧 直接上代码吧 let labelArr [直接访问, 邮件营销, 联盟广告, 视频广告, 搜索引擎]; let valueArr [{ value: 335, name: 直接访问 },{ value: 310, name: 邮件营销 },{ value: 234, name: 联盟广告 },{ value: 135, name: 视频广告 },{ value: 154, name: 搜索引…

Java8->Java19的初步探索

导读 最近网上开始了大量的关于Java19的讨论&#xff0c;我也想着用了Java8这么久该接受一点新的东西了&#xff0c;于是便开始研究了起来 Java 19 Java19是一个免费版本。下面是JDK19的支持图 image.png &#xff08;来源&#xff1a; https://www.bilibili.com/video/BV1V84…

软件设计师-上午题-15 计算机网络(5分)

计算机网络题号一般为66-70题&#xff0c;分值一般为5分。 目录 1 网络设备 1.1 真题 2 协议簇 2.1 真题 3 TCP和UDP 3.1 真题 4 SMTP和POP3 4.1 真题 5 ARP 5.1 真题 6 DHCP 6.1 真题 7 URL 7.1 真题 8 浏览器 8.1 真题 9 IP地址和子网掩码 9.1 真题 10 I…

nodejs批量修改word文档目录样式

工作中遇到一个需求:写个nodejs脚本,对word文档(1000+个)的目录页面进行美化。实现过程遇到不少麻烦,在此分享下。 整体思路 众所周知,Docx格式的Word文档其实是个以xml文件为主的zip压缩包,所以,页面美化整体思路是:先将文档后缀名改为zip并解压到本地,然后将关键的…

MathType在Word中的安装与配置记录

一、记录过程 1.MathType安装包下载 可直接下载本人已经安装过的安装包&#xff0c;亲测可以使用&#xff0c;下载链接如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1g-iOgKIqzSNz0E5rEUryug 提取码&#xff1a;1kb3 2.安装后配置 word中会出现mathtype的选项…

无人机之中继通信技术篇

一、定义与原理 无人机中继通信技术是指通过无人机搭载中继设备&#xff0c;将信号从一个地点传输到另一个地点&#xff0c;从而延长通信距离并保持较好的通信质量。其原理类似于传统的中继通信&#xff0c;即在两个终端站之间设置若干中继站&#xff0c;中继站将前站送来的信号…

轴流风机和后倾式风机的安装要求

后向离心风机风压大&#xff0c;风量足&#xff0c;安装方便。因为不需要蜗壳&#xff0c;所以风道往往需要自行设计&#xff0c;而风道的合理与否&#xff0c;大大影响了后向离心风机的效率。那么后向离心风机的安装技巧有哪些&#xff1f;怎样达到风机的最佳使用效果呢&#…

植物神经紊乱不用怕,这些维生素来帮你!

你是否经常感到身体疲惫、情绪波动大、心悸、胸闷&#xff1f;这可能是植物神经紊乱在作祟。别担心&#xff0c;通过合理的维生素补充&#xff0c;可以有效缓解症状&#xff0c;提升生活质量。今天&#xff0c;我们就来聊聊植物神经紊乱患者应该补充哪些维生素。 &#x1f50d…

使用C语言进行信号处理:从理论到实践的全面指南

1. 引言 在现代操作系统中&#xff0c;信号是一种进程间通信机制&#xff0c;它允许操作系统或其他进程向一个进程发送消息。信号可以用来通知进程发生了一些重要事件&#xff0c;如用户请求终止进程、硬件异常、定时器超时等。掌握信号处理技术对于开发健壮、高效的系统程序至…

LabVIEW配电产品精度测试系统

开发了一种基于LabVIEW平台的配电产品精度测试系统&#xff0c;通过自动化测试流程实现更高的测试准确性与效率。系统采用串口和TCP通信技术&#xff0c;与多功能交流采样变送器和配电设备无缝数据交互&#xff0c;提升了测试工作的可靠性和一致性。 一、项目背景 在配电产品…

基于JAVA SpringBoot和Vue社区网格化管理服务平台设计

摘要 本文旨在设计并实现一个基于Java SpringBoot和Vue技术的社区网格化管理服务平台。该平台主要包括用户功能和管理员功能两大部分&#xff0c;用户功能涵盖单位管理、问卷调查、论坛讨论、公告查看等&#xff1b;管理员功能则包括单位管理、基础数据维护、帖子和公告类型管…