【Linux线程】Linux线程编程基础:概念、创建与管理

📝个人主页🌹:Eternity._
⏩收录专栏⏪:Linux “ 登神长阶 ”
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀Linux多线程

  • 📒1. 线程概念
  • 📜2. 进程VS线程
  • 📚3. 线程控制
    • 🌊创建线程
    • 🍂线程终止
    • 🌸线程等待
    • 🌵线程分离
    • 🍁线程ID
  • 📖4. 总结


🔍前言:在Linux操作系统的广阔天地里,线程作为并发编程的基本单位,扮演着举足轻重的角色。它们如同操作系统的微观脉络,穿梭于各个任务之间,高效地协调着系统的运行。然而,要想驾驭好这些微观世界的精灵,对线程控制的深入理解是不可或缺的

我们深知,在多线程编程的复杂环境中,如何有效地管理线程,是确保程序稳定性和性能的关键所在。因此,本文将深入剖析Linux线程控制的核心概念,从线程的创建与终止我们将一一为您揭开它们的神秘面纱

我们力求做到理论与实践相结合。我们不仅会详细介绍线程控制的相关理论知识,还会通过丰富的实战案例,让您在动手实践中加深对线程控制技术的理解和掌握。我们相信,通过本文的学习,您将能够更加自信地面对多线程编程中的挑战,编写出更加高效、稳定的程序

让我们携手共进,共同探索Linux多线程编程的无限魅力吧!


📒1. 线程概念

线程是进程中的一个执行单元,或者说是进程内的一条执行路径、一个执行流。它是被系统独立调度和分派的基本单位,负责执行进程中的代码。每个线程都有自己独立的线程ID、程序计数器、寄存器集合以及栈空间,但它们共享同一个进程的地址空间和其他资源,如全局变量、静态变量、堆内存等

在这里插入图片描述

让不同PCB指向同一块地址空间,共享进程的资源,线程在进程的地址空间中运行,线程就是一种类似与进程的轻量级进程,但是线程是一个没有独立的地址空间的PCB结构,线程切换效率高

注意:线程是CPU调度的基本单位,进程是承担系统调用的基本实体


  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

在这里插入图片描述
Cache:

Cache是位于CPU和主存储器(DRAM)之间的一块高速缓冲存储器,规模较小但速度非常快,通常由SRAM(静态存储器)组成。其主要功能是暂时存储CPU即将访问的数据,从而提高CPU数据输入输出的速率。当CPU需要访问数据时,会首先在Cache中查找,如果找到所需数据,则直接从Cache中读取,避免了访问速度较慢的主存,从而提高了系统的整体性能

线程切换效率高的原因:

  • 寄存量少
  • 不需要重新更新Cache

线程的优点:

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

线程的缺点:

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

线程异常:

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出

线程用途:

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验

📜2. 进程VS线程

进程是资源分配的基本单位,线程是调度的基本单位,线程共享进程数据,但也拥有自己的一部分数据

  • 线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级

进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到

除了上面,各线程还会共享进程的其他资源:

  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

在这里插入图片描述


📚3. 线程控制

POSIX线程库定义了一套用于创建、操纵和管理线程的API。这些API允许程序员在Unix-like系统(如Linux、Solaris)上编写多线程程序

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文件<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项

如何查看线程:

指令:ps -aL

在这里插入图片描述


🌊创建线程

pthread_create:

在这里插入图片描述

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

参数说明:

  • thread:返回线程ID
  • attr:设置线程的属性,attr为NULL表示使用默认属性
  • start_routine:是个函数地址,线程启动后要执行的函数
  • arg:传递给线程函数的参数,可以是任何类型的数据,但需要通过类型转换与线程函数中的参数类型匹配,也可以传一个类

返回值:成功返回0;失败返回错误码

代码示例:

#include <iostream>
#include <unistd.h>
#include <pthread.h>void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);while(1){cout << "new thread" << ", thread name: " << threadname << endl;sleep(1);}}int main()
{pthread_t tid;ThreadData *td = new ThreadData("thread_1", (uint64_t)time(nullptr), Print);pthread_create(&tid, nullptr, ThreadRountine, td);while(1){cout << "I am a main thread" << endl;sleep(1);}return 0;
}

🍂线程终止

在多线程编程中,线程终止(Thread Termination)是指一个线程结束其执行过程,释放相关资源,并退出其生命周期。线程终止可以是由于线程正常完成其任务,也可以是由于某些异常情况或外部请求导致的提前结束

只终止某个线程而不终止整个进程:

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

pthread_exit:

在这里插入图片描述

代码示例:(运行5秒后退出)

void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while(cnt--){cout << "new thread -> name: " << threadname << endl;sleep(1);}// return nullptr;pthread_exit(nullptr);
}

在这里插入图片描述


pthread_cancel:

在这里插入图片描述

int pthread_cancel(pthread_t thread);

参数:

  • thread:线程ID

代码示例:

void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while(cnt--){cout << "new thread -> name: " << threadname << endl;sleep(1);}cout << "other thread cancel done" << endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");sleep(5);// pthread_detach(tid);int n = pthread_cancel(tid);cout << "main thread done" << ", n: " << n << endl;void *ret = nullptr;n = pthread_join(tid, &ret);cout << "main thread join done" << " n: " << n << ", thread return: " << (int64_t)ret << endl;return 0;
}

在这里插入图片描述

线程如果是被分离的,该线程可以被取消,但是不能被等待


🌸线程等待

在Linux或多线程编程环境中,线程等待通常指的是一个线程暂停其执行,直到满足某个特定条件或另一个线程完成某个任务后再继续执行

pthread_join:

在这里插入图片描述

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

参数:

  • thread:线程ID
  • value_ptr:它指向一个指针,后者指向线程的返回值

代码示例:

void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while(cnt--){cout << "new thread -> name: " << threadname << endl;sleep(1);}cout << "other thread done" << endl;// return nullptr;pthread_exit(nullptr);
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");sleep(10);int n = pthread_join(tid, nullptr);cout << "main thread done" << " n: " << n << endl;sleep(5);return 0;}

在这里插入图片描述

如果我们想获取线程的返回值,我们要注意pthread_join的第二个参数是void **类型,而ThreadRoutine的返回值为void *,因此我们要对格外注意

获取线程的返回值:

void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while(cnt--){cout << "new thread -> name: " << threadname << endl;sleep(1);}cout << "other thread done" << endl;// return nullptr;return (void *)"thread_1 done";
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");void *ret = nullptr;	int n = pthread_join(tid, &ret); // 注意这里要&ret	cout << "main thread done" << " n: " << n << "thread done and return: "<< (const char*)ret << endl;return 0;}

在这里插入图片描述


🌵线程分离

分离线程是多线程编程中的一个重要概念,它指的是将一个线程从主线程或创建它的线程中分离出来,使其能够独立运行,并且不再需要其他线程使用特定的函数(如pthread_join())来等待其结束

pthread_detach:

在这里插入图片描述

int pthread_detach(pthread_t thread);

参数:

  • thread 是你想要分离的线程的标识符(线程ID)

返回值:如果成功,pthread_detach 返回 0。如果失败,它返回一个错误码


代码示例:(在线程分离后等待线程)

// 线程分离
void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while(cnt--){cout << "new thread -> name: " << threadname << endl;sleep(1);}cout << "other thread done" << endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");sleep(1);pthread_detach(tid);int n = pthread_join(tid, nullptr);cout << "main thread done" << " n: " << n << endl;return 0;
}

在这里插入图片描述

在线程分离后等待线程,线程会直接返回一个错误码


🍁线程ID

线程ID本质是一个地址

void *ThreadRoutine(void *args)
{string threadname = static_cast<const char*>(args);while(1){cout << "new thread -> name: " << threadname << endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");cout << "tid: " << tid << endl;while(1){cout << "I am a main thread" << endl;sleep(1);}return 0;}

在这里插入图片描述


pthread_self:

功能:可以获得线程自身的ID

在这里插入图片描述

pthread_t pthread_self(void);

返回值:pthread_self 函数返回一个类型为 pthread_t 的值,这个值唯一地标识了调用它的线程。pthread_t 通常是一个整数或结构体,用于表示线程标识符

代码示例:

int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread_1");cout << "pthread_t id: " << pthread_self() << endl;return 0;
}

在这里插入图片描述
线程库要想管理线程,那么它必须要先被加载到地址空间中的mmap区域,线程库是共享的,内部要管理整个系统的,多个用户启动的所有线程

对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址,为了方便我们的库直接找到内存,而pthread_t ID就是pthread的首地址,pthread库要自己维护线程这块栈区

在这里插入图片描述

  • 线程可以通过函数fork来创建子进程,但是线程不能进行进程程序替换,因为线程是共用主线程的资源,一旦一个线程进行进程程序替换后,所有的线程包括主线程的代码都会被替换为别的程序

📖4. 总结

在探索Linux线程控制的旅程中,我们不仅解锁了并发编程的强大潜力,还深刻理解了线程作为操作系统调度基本单位的核心价值。从线程的创建与终止巧妙运用,再到线程属性的精细调整,每一步都充满了挑战与收获

通过本文的引导,希望每位读者都能掌握一套系统化的线程控制方法论,无论是对于初学者踏入并发编程的大门,还是对于经验丰富的开发者深化对Linux线程机制的理解,都能有所裨益。记住,技术虽不断进步,但对基础概念的深刻理解永远是创新与优化的基石

未来,随着多核处理器架构的普及和云计算、大数据等领域的快速发展,Linux线程控制的重要性将愈发凸显。它不仅是构建高性能应用的必备技能,更是深入理解现代操作系统内部机制的关键一环。让我们带着这份知识与经验,继续在技术的海洋中航行,不断探索、实践与创新,共同迎接更加辉煌的技术未来

愿每位程序员都能在并发编程的世界里,编织出属于自己的精彩篇章!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

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

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

相关文章

算法魅力-双指针的实战

目录 1.双指针的介绍 1. 左右指针&#xff08;对撞指针&#xff09; 2. 快慢指针 2.题目练习讲解 2.1 移动零 算法思路 代码展示 画图效果效果 2.2 复写零 算法思路 代码展示 2.3 快乐数 算法思路 代码展示 2.4 盛最多水的容器 算法思路 代码展示 结束语 1.双指针的…

大模型带来新安全机遇

当前网络空间安全面临攻击隐蔽难发现、数据泄露风险高和违法信息审核难等挑战。大模型展现出强大的信息理解、知识抽取、意图和任务编排等能力&#xff0c;为网络空间安全瓶颈问题提供了新的解决思路和方法。与此同时&#xff0c;大模型发展也催生了恶意软件自动生成、深度伪造…

架构师之路-学渣到学霸历程-22

NFS文件共享服务器 今天开始了云计算-SRE架构师的第二个阶段&#xff0c; 第二阶段就是服务阶段了&#xff1b;第一个分享的就是NFS服务&#xff1b; 文件共享服务&#xff1b; 早上就了解一下NFS原理&#xff1b; 1、NFS文件共享服务器 NFS&#xff1a;就是network file sy…

【YOLO学习】YOLOv5详解

文章目录 1. 网络结构2. 结构整体描述2.1 输入端2.2 Backbone2.3 Neck2.4 Head 3. 模块细节3.1 Focus模块3.2 SPPF3.3 Bounding Box损失函数 4. 训练策略 1. 网络结构 1. 目标检测的模型框架大体都是以下图示这样的结构&#xff1a; 2. 关于 YOLOv5 的网络结构其实网上相关的讲…

数据结构 - 队列

队列也是一种操作受限的线性数据结构&#xff0c;与栈很相似。 01定义 栈的操作受限表现为只允许在队列的一端进行元素插入操作&#xff0c;在队列的另一端只允许删除操作。这一特性可以总结为先进先出&#xff08;First In First Out&#xff0c;简称FIFO&#xff09;。这意味…

R语言机器学习算法实战系列(八)逻辑回归算法 (logistic regression)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总结系统信息介绍 …

MySQL数据库和表的基本操作

文章目录 一、数据库的基础知识 背景知识数据库的基本操作二、数据类型 字符串类型数值类型日期类型三、表的基本操作 创建表查看表结构查看所有表删除表 一、数据库的基础知识 背景知识 MySQL是一个客户端服务器结构的程序 主动发送数据的这一方&#xff0c;客户端(client…

“智改数转”转了什么?

万界星空科技专门针对数字化改造申报的MES系统具有显著的技术优势和实施效果&#xff0c;能够为制造型企业提供全方位、高效、可靠的数字化转型支持。项目合作可以私信或者百度上海万界星空科技官网。 “智改数转”是一个综合性的过程&#xff0c;涉及企业多个方面的转型和升…

【python实战】利用代理ip爬取Alibaba海外版数据

引言 在跨境电商的业务场景中&#xff0c;数据采集是分析市场、了解竞争对手以及优化经营策略的重要环节。然而&#xff0c;随着越来越多企业依赖数据驱动决策&#xff0c;许多跨境电商平台为了保护自身数据&#xff0c;采取了更严格的防护措施。这些平台通过屏蔽大陆IP地址或部…

【Spring声明式事务失效的12种场景测试】

文章目录 一.Spring声明式事务是什么&#xff1f;二.Spring事务失效的12种场景1.访问权限问题 小结 一.Spring声明式事务是什么&#xff1f; Spring声明式事务是一种通过配置的方式管理事务的方法&#xff0c;它通过注解或XML配置来声明哪些方法需要事务管理&#xff0c;从而将…

JRT怎么从IRIS切换到PostGreSql库

1.执行M导出得到建库脚本文件 2.下载生成的脚本到本地D盘 3.修改驱动为PostGreSql 4.修改连接串 5.到PostGreSql里面创建一个jrtlis的数据库&#xff0c;模式为jrt 6.启动网站点击导入脚本按钮 导入完成了就可以正常使用PostGreSql库了

OpenCV高级图形用户界面(14)交互式地选择一个或多个感兴趣区域函数selectROIs()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 允许用户在给定的图像上选择多个 ROI。 该函数创建一个窗口&#xff0c;并允许用户使用鼠标来选择多个 ROI。控制方式&#xff1a;使用空格键或…

Google FabricDiffusion:开启3D虚拟试穿新篇章

随着数字化转型的步伐不断加快,时尚界也在探索如何利用最新技术为消费者带来更加沉浸式的购物体验。在这一背景下,Google 推出了一项名为 FabricDiffusion 的新技术,这项技术能够将2D服装图像中的高质量织物纹理转移到任意形状的3D服装模型上,从而为3D虚拟试穿提供了更为真…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于AGCN-LSTM模型的海上风电场功率概率预测 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

端到端自动驾驶模型SparseDrive部署过程

SparseDrive 论文链接 https://arxiv.org/pdf/2405.19620 仓库链接 https://github.com/swc-17/SparseDrive 论文和模型的相关介绍大家可以参考其他博客的介绍&#xff0c;这里只介绍模型部署的过程和中间可能遇到的问题解决办法&#xff0c;以及代码解析和使用记录。 模型部署…

CyberRT通信介绍与基于Reader、Writer的通信实践(apollo9.0)

目录 数据通信场景 CyberRT中的通信方式 ​编辑 通信模式 话题通信 服务通信 参数通信 protobuf protobuf简介 protobuf文件编写 topic通信实验 实验环境 实验准备 代码编写 定义消息格式 发送消息 接收消息 定义编译规则 程序编译 运行程序 数据通信场景 …

fabric-sdk-go

Fabric-SDK-go 区块链网络搭建fabric-sdk代码代码结构&#xff1a;代码eg&#xff1a; 区块链网络搭建 使用fabric-sample的网络结构用容器搭建起测试网络即可。 fabric-sdk代码 代码很简易&#xff0c;主要为了了解怎么使用fabric为编程人员提供的sdk从而提供HTTP接口的情况…

浅谈华为 HarmonyOS Next

1. 万物互联时代的新机遇 随着万物互联时代的到来&#xff0c;智能应用从几十亿部手机扩展到数百亿个IoT设备&#xff0c;深刻改变了人们的生活方式。这为我们应用开发者带来了新的机遇和挑战。 机遇 : 目前正处于万物互联时代的前夕&#xff0c;正在经历手机单设备到全场景多…

技术分享:A-23OH型树脂在汽车涂装废溶剂回收中的应用

在当今汽车制造业竞争激烈的环境下&#xff0c;提高生产效率、降低成本的同时&#xff0c;满足环保要求已成为各制造商追求的核心目标。水性涂料因其环保、节能等多重优势&#xff0c;在汽车涂装领域的应用日益广泛。然而&#xff0c;随之而来的喷涂废溶剂处理问题也日益凸显。…

从 Hadoop 迁移到数据 Lakehouse 的架构师指南

从 Hadoop 到数据湖仓一体架构的演变代表了数据基础架构的重大飞跃。虽然 Hadoop 曾经以其强大的批处理能力统治着大数据领域&#xff0c;但如今的组织正在寻求更敏捷、更具成本效益和现代化的解决方案。尤其是当他们越来越多地开始实施 AI 计划时。根本没有办法让 Hadoop 为 A…