【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线程控制的重要性将愈发凸显。它不仅是构建高性能应用的必备技能,更是深入理解现代操作系统内部机制的关键一环。让我们带着这份知识与经验,继续在技术的海洋中航行,不断探索、实践与创新,共同迎接更加辉煌的技术未来

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

在这里插入图片描述

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot实现接口:统一返回值、全局异常处理、Swagger接口文档

在 Spring Boot 应用中实现统一返回值和全局异常处理可以带来多方面的好处&#xff0c;这些好处不仅提升了代码的可读性和可维护性&#xff0c;还增强了应用的健壮性和用户体验。以下是一些具体的好处&#xff1a; 代码一致性&#xff1a; 通过定义统一的返回值格式&#xff…

【从零开始的LeetCode-算法】3194. 最小元素和最大元素的最小平均值

你有一个初始为空的浮点数数组 averages。另给你一个包含 n 个整数的数组 nums&#xff0c;其中 n 为偶数。 你需要重复以下步骤 n / 2 次&#xff1a; 从 nums 中移除 最小 的元素 minElement 和 最大 的元素 maxElement。将 (minElement maxElement) / 2 加入到 averages …

Apache Linkis + OceanBase:如何提升数据分析效率

计算中间件 Apache Linkis 构建了一个计算中间件层&#xff0c;以实现上层应用程序和底层数据引擎之间的连接、治理和编排。目前&#xff0c;已经支持通过数据源的功能&#xff0c;实现用户通过Linkis 对接并使用 OceanBase数据库。 本文详细阐述了在 Apache Linkis v1.3.2中&a…

零基础学习网络安全,注意这几个高效学习方法,零基础入门到精通,收藏这篇就够了

零基础学习网络安全&#xff0c;注意查收这些有效学习方法 ‍零基础怎么学网络安全?网络安全学习办法有很多&#xff0c;又高效得&#xff0c;也有低效得&#xff0c;还有无效得。今天和我一起来看看网络安全的有效学习秘籍。 ‍ 零基础从什么开始学网络安全&#xff0c;大…

七大经典排序算法优化:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序、归并排序代码详解

目录 排序算法 1.插入排序 2.希尔排序 3.选择排序 4.冒泡排序 5.堆排序 6.快速排序 7.归并排序 排序算法 排序算法是一类用于将数据按照特定顺序&#xff08;如升序或降序&#xff09;排列的算法&#xff0c;常用于优化数据检索和处理。常见的排序算法包括冒泡排序、选…

【Deepin】钉钉下载文件图片闪退问题解决(临时方式)

环境 故障 下载文件、图片等闪退 解决 cd /opt/apps/com.alibabainc.dingtalk/files/7.6.0-Release.4091801/sudo rm -rf ./libstdc.so.6*注&#xff1a; 7.6.0-Release.4091801可能会略有不同&#xff0c;根据实际情况调整保险起见&#xff0c;操作第二行删除命令时&#…

第二十七篇:传输层讲解,TCP系列一

一、传输层的功能 ① 分割与重组数据 传输层也要做数据分割&#xff0c;所以必然也需要做数据重组。 ② 按端口号寻址 IP只能定位数据哪台主机&#xff0c;无法判断数据报文应该交给哪个应用&#xff0c;传输层给每个应用都设置了一个编号&#xff0c;这个编号就是端口&…

Wails 学习笔记:Wails核心思想理解

文章目录 1. Wails 的核心思想2. 工作流程2.1 前端渲染2.2 后端逻辑2.3 前后端通信2.4 应用打包与分发 3. Wails 主要组件3.1 WebView3.2 事件与数据绑定3.3 窗口管理 4. Wails 的优点5. Wails 的使用场景6. 启动函数Runwails.Run() 的主要功能wails.Run() 的参数&#xff1a;w…

【C++】STL篇 string类(使用)

string的学习会分为两个大步骤&#xff0c;第一步就是会使用string&#xff0c;第二部是模拟实现string。这篇文章我们介绍一下string类以及它的使用。string大概有一百多个接口&#xff0c;我们需要重点掌握的就十几二十个。string其实就是字符串&#xff0c;严格来说string类…

STM32传感器模块编程实践(八) HX711压力传感器称重模块简介及驱动源码

文章目录 一.概要二.HX711主要技术指标三.HX711模块参考原理图四.模块接线说明五.模块工作原理介绍六.模块通讯协议介绍七.STM32单片机与HX711模块实现重量测量实验1.硬件准备2.软件工程3.软件主要代码4.实验效果 八.小结 一.概要 电子秤是将检测与转换技术、计算机技术、信息…

一文通透OpenAI o1:从CoT、Quiet-STaR、Self-Correct、Self-play RL、MCST等技术细节到工程复现

前言 注意&#xff0c;本文自10.12日起&#xff0c;正在每天更新的过程中&#xff0c;包括已写的部分也在不断修改(以增加更多技术细节、更加通俗易懂) 预计10.20完成第一版&#xff0c;10月底修订到第二版——具体修订记录详见本文文末.. 可能是去年写或讲的关于ChatGPT原理的…

植物大战僵尸杂交版之后要出联机版植物大战僵尸?(内测中,可在安卓手机上玩,文末附下载链接)

继植物大战僵尸杂交版之后给大家介绍一个杂交版作者正在酝酿的“植物大战僵尸射击版” 植物大战僵尸射击版介绍 《植物大战僵尸杂交版》的创作者“潜艇伟伟迷”即将推出PVZ改版新作——《植物大战僵尸射击版》。游戏将支持PC、手游和web端&#xff0c;提供单人、双人、三人、…

【java Web如何开发?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

华为eNSP实验:交换机流量控制之风暴控制

一、交换机流量控制之风暴控制 风暴控制是交换机流量控制中的一种重要机制&#xff0c;用于防止网络中的广播、多播或单播风暴对网络性能造成破坏。具体如下&#xff1a; 基本原理&#xff1a;风暴控制通过监控端口的入站流量&#xff0c;并与预设的风暴抑制级别进行对比来管…

java数组讲解

前言&#xff1a; 由上两章&#xff0c;我们已经了解关于java的基础语法&#xff0c;这章我们将讲解数组的相关语法&#xff0c;坐好了没&#xff0c;我们准备要发车啦&#xff01;&#xff01;&#xff01; 我们将从五部分给大家讲解&#xff1a; 1数组的基本概念 2.数组是…

使用Windows创建一个MFC应用【带界面】

MFC使用教程【对初学者保姆型友好&#xff01;】 目录 前提条件 1&#xff1a;创建MFC应用程序 2. 项目结构解读 引用 外部依赖项 头文件 源文件 资源文件 文件功能详解 项目的主要流程 步骤2&#xff1a;配置OpenCV 安装OpenCV 包含目录与库文件 步骤3&#xff1…

Milvus×Dify半小时轻松构建RAG系统

最近&#xff0c;检索增强生成&#xff08;RAG&#xff09;技术在AI界引起了广泛关注。作为一种将知识库与生成模型结合的新型架构&#xff0c;RAG大大提升了AI应用的实际表现。而在构建RAG系统时&#xff0c;Milvus作为业界领先的开源向量数据库&#xff0c;扮演着关键角色。本…

视频格式在线转换,五种超实用的视频格式转换工具!

视频内容无处不在&#xff0c;从教育课程到娱乐电影&#xff0c;从社交媒体分享到在线会议&#xff0c;视频已成为我们日常生活中不可或缺的一部分。然而&#xff0c;不同的设备和平台支持的视频格式各异&#xff0c;会导致视频文件在某些设备上无法播放。因此&#xff0c;掌握…

计算机毕业设计python+spark知识图谱课程推荐系统 课程预测系统 课程大数据 课程数据分析 课程大屏 mooc慕课推荐系统 大数据毕业设计

指导教师意见&#xff1a; 1&#xff0e;对“文献综述”的评语&#xff1a; 对教育领域数据可视化的相关背景和现状做了综述&#xff0c;明确了课题的研究目标和研究重点&#xff0c;并对研究手段进行了概述。为后面的毕业设计做好了准备。 对本课题的深度、广度及工作量的…

【开源】第三期:数字货币程序化交易终端开源

关于初衷&#xff1a; 这篇文章&#xff0c;其实应该在六年前发出来&#xff0c;但是受制于各种杂事和生活琐事&#xff0c;一直拖到现在&#xff0c;想必有朋友看到在"终端"那期里&#xff0c;聊到的数字货币交易的实践&#xff0c;那个时候遍地都是数字货币交易所&…