Linux相关概念和易错知识点(29)(线程相关性质和操作、线程封装)

Linux C 中的 线程 文章目录1. 概念1.1 为什么要用线程1.2 线程与进程的关系 1. 概念 1.1 为什么要用线程 由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大 为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程 在同一个进程中创建的线程共享该进程的地址空间 Linux里同样用task_struct来描述一个线程。线程和进程都参与统一的调度 线程称之为轻量级的进程,多个线程共用同一个进程的资源 多个进程在运行的时候是来回切换执行的,如果要从一个进程切换到另一个进程执行,那么 阅读详情

目录

1.线程相关性质和操作

(1)LWP的查看

(2)资源划分

①时间资源

②共享资源

2.线程封装

(1)封装后的效果

(2)类的设计及其实现

①成员变量的设计

②成员函数的设计

(3)全部代码


1.线程相关性质和操作

(1)LWP的查看

当创建了多个线程后,我们希望在bash中查看这些线程的属性。

我们发现ps -ajx只能以进程的尺度来查看,想要得到轻量级进程的相关属性,需要使用ps -aL

这个时候我们就能查看到当前系统的LWP属性了。PID自不必说,就是进程的id,用于标识唯一的进程。上图所示两个都是同一进程下的LWP。我们接着看LWP,469053和469054,显然前者为主线程,后者是新线程,主执行流的LWP和其PID相同。但有个问题,这些LWP值和左侧打印的线程的id值没有关系啊,这是为什么?

在Linux里面没有线程,只有轻量级进程,pthread进行了上层封装向我们提供接口以使用线程。不仅如此,所有线程的属性也都做了上层封装,在struct pthread里面,由pthread.so维护,这是上篇文章提到的。而这里是以系统层级查看LWP,这个值和pthread上层封装后的线程id肯定是不一样的。在系统层面根本没有线程,也不存在线程的属性,我们pthread_self()打印的值是库维护的,而不是系统的,因此这个LWP和线程id值不一样。

我们还能发现主执行流的LWP和PID的值一致,新线程的LWP是紧接着的数字。这说明OS调度看到的其实是LWP,PID其实是第一个主执行流的LWP的值,我们之前因为都是单线程没发现这一点。对于OS来说,区分执行流的是LWP而不是PID,PID从LWP中来,作为标识整个进程的id。当然,task_struct里面肯定也存着PID和LWP,标识这该执行流属于那个进程。对于单线程来说,LWP == PID。

只要彻底理解了Linux下线程的概念,这些值的分配就很好理解了。

(2)资源划分

①时间资源

对于Linux来说,新线程和主线程谁先运行不确定。另外,线程会瓜分进程的时间片资源,但不会为线程多争取时间片资源。这意味着给一个进程的时间片始终是恒定的,不会因为增加线程的数量而增加进程的时间片,不然就会发生通过增加线程数来获取时间的混乱。

②共享资源

不同线程可重入一个函数,即多个线程同时进入函数。如果是打印操作,那么打印时就会发生混乱,因为多线程访问公共的显示器文件会发生数据不一致、竞争的问题。在这种情况下,显示器文件就是共享资源,需要同步等操作来保护,后续会专门展开讲解。

2.线程封装

只要理解了线程控制的基本操作,基本的线程封装就很快能写出来了,我简要讲解思路即可。

(1)封装后的效果

线程封装不难,但一定要搞清楚为什么封装?封装后有什么特性?这个thread类在整个封装中有什么功能和作用?实例化的对象充当什么角色?这个对象是如何管理线程的?这结合了C++和Linux系统的某些思想,需要好好体会。

C++是一个面向对象的语言。我们使用类封装线程的控制后,希望一个对象即可管理一个新线程。我们只需要调取它的成员函数就可以对这个新线程操作。这就像先描述、再组织那样,对一个线程的管理转为对一个实例化对象的管理。

下面是封装完后的整体简单使用

(2)类的设计及其实现

①成员变量的设计

在设计中,我们秉承着用一个对象来管理一个线程的思想。这就像先描述、再组织那样,要存储线程相关的属性,包括线程名字_name,它正处于的状态_state等。注意这个类只是描述线程的,它只是线程外部管理的壳子。

其状态和函数包装器的设计如下

我们这次的简单设计只考虑无参的函数,重点在于体会封装的过程。

②成员函数的设计

对于构造函数,就是对线程的各种属性先做初始化。注意这个时候线程实际上还没有被创建出来。我们一定不能搞混,线程封装的对象是用来管理、描述线程的,这个对象本身不是一个新线程,因此创建这个新对象不意味着已经创建了一个新线程。很多对线程封装感到别扭就是在这里理解有误。

start函数这里才算是真正创建了线程。其原理是用户拿着对象进行t.start()时,这个对象调用成员方法创建一个新线程,并且将这个新线程的tid自动保存在成员变量里,以便后续维护。对于用户而言,它无需知道这个新线程的tid即可管理线程,因为这个类已经帮我们管理起来了,仔细体会这层封装。并且我们要知道这个对象调用其成员函数的整个过程都是在主线程中执行的,只是在类域里面执行函数,创建线程还是算作主线程创建的。

对于join函数来说,就是要让当调用t.join()时,让调用方主线程等待新线程,同时更新_joinable到属性里面防止被join两次。

当main函数那里调用t.join时,主线程会到类域里面来执行函数,所以实际上是主线程调用的pthread_join函数,是主线程被阻塞等待,整个过程中类不会对线程的执行产生什么影响。

其余代码都很简单,最后展示

(3)全部代码


#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <functional>


using namespace std;

namespace myThread
{
    static int count = 1;

    enum STATE
    {
        INIT,
        RUNNING,
        CANCEL,
        FINISH
    };

    class thread
    {
        using func_t = function<void()>;

        static void* execute(void* p)
        {
            static_cast<thread*>(p)->_fun();
            return nullptr;
        }

    public:
        thread(void(*pfun)())
        :_state(INIT)
        ,_tid(-1)
        ,_fun(pfun)
        ,_joinable(true)
        {   
            _name = "thread-" + to_string(myThread::count++);
            printf("对象已创建\n");
        }

        bool start()
        {
            if(_state == INIT)
            {
                pthread_create(&_tid, nullptr, execute, (void*)this);
                _state = RUNNING;
                printf("0x%lx线程已开始运行\n", _tid);
                return true;
            }

            printf("0x%lx线程已被创建,不能重复启动\n", _tid);
            return false;
        }

        
        bool joinable()
        {
            return _joinable;
        }

        bool join()
        {
            if(_joinable)
            {
                if(pthread_join(_tid, nullptr) == 0)
                {
                    printf("0x%lx线程已被join,返回值已自动抛弃\n", _tid);
                    _joinable = false;
                    _state = FINISH;
                    return true;
                }
            }

            printf("join失败\n");
            return false;
        }

        bool cancel()
        {
            if(_state == RUNNING)
            {
                if(pthread_cancel(_tid) == 0)
                {
                    printf("0x%lx线程已被cancel\n", _tid);
                    _state = CANCEL;
                    return true;
                }
            }

            printf("cancel失败\n");
            return false;
        }

        bool detach()
        {
            if(_state == RUNNING)
            {
                if(pthread_detach(_tid) == 0)
                {
                    printf("0x%lx线程已分离\n", _tid);
                    return true;
                }
            }

            printf("detach失败\n");
            return false;
        }


    private:
        string _name;
        myThread::STATE _state;
        pthread_t _tid;
        func_t _fun;
        bool _joinable;
        
    };

}

Linux线程信号量 信号量的本质实际上就是一把计数器,可以用来描述临界资源中资源数目的大小,即最多能有多少资源可以分配给线程。 临界资源可以被划分为一个一个的小资源,如果处理得当,有可能让多个线程同时访问临界资源的不同区域,从而实现并发。每个线程想访问临界资源,都需要先申请信号量资源,使用这个计数器可以实现对临界资源的预定机制。 信号量也是一种特殊的数据类型—sem_t,信号量需要对其进行初始化操作,上图是对信号量进行初始化的函数,使用该参数需要控制的只有第一个和第三个参数 即第一个参数传入需要进行初始化的信号量 第二个参数p 阅读详情

相关推荐

如何修复Windows卡在“正在准备Windows”的问题?这里有详细步骤

如何修复Windows卡在“正在准备Windows”的问题?这里有详细步骤。

wyxtx的博客 5659

Linux相关概念和易错知识点(31)(生产者-消费者模型、认识信号量、线程安全)

形象的说法是,申请别人不会释放的资源,并且自己也不释放资源、僵持不下的状态就叫死锁。举个例子,B申请A的锁,并且说它得到A的锁后就会释放自己的锁;与此同时,A也在申请B的锁,并且说它申请到B的锁后才会释放自己的锁。它们两个线程谁也不让谁,这就陷入了死锁。单线程也可能产生死锁,一把锁也可能把自己锁住,如连续两次申请锁。当第二次申请锁时,这个线程的右手想要拿左手正在拿着的锁,左手对右手说它需要执行到unlock才会释放,而右手说它马上就要这把锁,不然不让走。这就陷入了死锁。

SGlow0708的博客 1163

自学PCB思路 | PCB设计零基础入门-小白适用(附详细入门经验分享)

本人大一在实践课上就听说PCB,被虎的一楞一楞的,不过现在回看,发现其实并没有相信的那么难!想着把自己这一路摸索的学习路线分享一下,当时我是一边摸索一边上手,一把鼻涕一把泪的过来的。如果你想搞硬件或者需要搞硬件,下面是我的入门学习过程,希望可以帮到你。目录前言选择一款设计软件好的入门课程掌握抄板元件采购焊板子烙铁头热风枪自己半设计明确设计需求全设计最后我并非什么大佬,也没法自己去教大家如何画板。

自动化小秋葵的博客 1万+

Linux相关概念和易错知识点(36)(TCP套接字编程、远程命令的实现)

远程命令,即让远程主机返回本地输入的结果。和聊天室的逻辑几乎一致。有的命令很危险,可以通过黑名单(哪些不能执行)和白名单(哪些命令可以执行)来实现保护。

SGlow0708的博客 1005

MDT部署实战:除了Office,这些“装机必备”软件也能一键静默安装(含静默参数)

本文详细介绍了如何利用Microsoft Deployment Toolkit(MDT)实现企业级软件的静默安装,包括浏览器、开发工具和系统增强工具等“装机必备”软件的部署方案。通过命令行参数和MSI属性,IT运维团队可以高效构建标准化工作环境,提升部署效率并确保版本一致性。文章还提供了安装顺序控制、结果验证和常见问题排错等高级技巧。

weixin_26985923的博客 171

CAN收发器TJA1145A休眠唤醒应用

TJA1145的唤醒工作原理主要依赖于其内部的唤醒机制和外部硬件的配合,主要涉及低功耗状态、选择性唤醒功能。TJA1145是一款CAN收发器,它支持多种工作模式,包括NormalStandbySleep等。在这些模式中,Sleep模式是最低功耗状态,而Normal模式则是最活跃的状态,其中Standby模式处于两者之间,允许一定的功能操作但功耗较低。TJA1145可以通过接收特定的唤醒源从Sleep或Standby模式唤醒到Standby或Normal模式。唤醒源的检测与处理。

radix关于mcu的博客,持续更新中... 5948

【Linux】线程控制函数

线程控制函数的使用以及扩展的线程内容:c++线程库和每个线程的独立栈结构

s_little_monster的博客 9017

康耐视visionpro-CogBlobTool工具详细说明

康耐视visionpro-CogBlobTool工具详细说明

视觉人机器视觉的博客 1724

线程互斥【Linux操作系统】

因为它会变成3条汇编指令,一条汇编指令虽然是原子的,但是3条汇编指令和在一起的操作就不是原子的了所以在CPU在执行这3条汇编指令期间,都有可能进行线程切换。比如:ticketnum=1了,线程a把1读取到寄存器之后,线程a就切换了,还没去–ticketnum线程b也来读取了,也把ticketnum=1读到寄存器里了这个时候,线程a和线程b就都会判断,ticketnum>0,就都进去抢了而且。

2301_80058734的博客 1208

8、干式变压器

干式变压器的发展,是人类不断挑战材料科学和热物理极限的过程,每一次技术突破,都是在。温度升高,ε_r反而下降(因为分子活动性增强,极化响应变慢)。:固体导热的热阻比液体对流大一个数量级,这是无法逾越的物理规律。Nomex在高温下不是逐渐分解,而是保持性能直到一个临界点。如果 T_a = 40°C,则 T_c = 146.7°C。干式变压器(固体介质分隔):热场、电场、流场(空气)形成三维氢键网络,即使在高温下也能保持机械强度。:不是在现有技术上修修补补,而是寻找新的。仍在H级绝缘允许范围内(≤180°C)

renpeng2692873032的博客 228

HDR 成像技术学习(三)—— LOFIC

LOFIC(Lateral Overflow Integration Capacitor,横向溢出集合电容)介绍,从像素结构到工作时序。

沧海一升的博客 9729

Linux->多线程1

Linux->多线程1 (线程概念->线程理解->二级页表->线程控制->线程ID的本质->线程总结->线程VS进程->模拟封装C++线程)

shylyly_的博客 828

YOLOv11改进策略【Backbone/主干网络】|替换HGNetV2骨干,包含RT-DETR中的l、x模型

本文记录的是基于HGNetv2的YOLOv11目标检测改进方法研究。本文利用HGNetv2替换YOLOv11的骨干网络,HGNetv2 通过其 GPU 推理友好的 3×3 标准卷积和层次化 HG Block 结构,能够为骨干网络提供高效且完整的特征提取能力。并且其中的和可在降低计算量的同时丰富特征表达,增强对多尺度目标特征的捕获能力,进而提高模型精度。本文配置了原论文中rtdetr-lrtdetr-x,以满足不同算力和检测精度的需求。

Limiiiing的博客 669

操作系统01——线程的概念以及Linux下的创建方法

一、线程概念 进程 进程是程序的一次执行 线程 线程(thread)是操作系统能够进行运算调度的最小单位。 它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 线程是独立调度和分派的基本单位。 同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储

winterjojo的博客 298

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

linux线程

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。一切进程至少都有一个执行线程。线程在进程内部运行,本质是在进程地址空间内运行。在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。下面我们先来看一下之前我们学习的进程地址空间。

dong132697的博客 378

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Linux线程同步之条件变量和信号量

本文系统介绍了线程同步机制中的条件变量和信号量。条件变量部分详细讲解了其工作原理及核心函数,包括pthread_cond_wait、pthread_cond_signal等,并附有示例代码演示线程同步实现。信号量部分区分了二进制信号量和计数信号量的应用场景,对比了有名信号量和无名信号量的特点及其API函数(如sem_init、sem_open等)。通过多个实际代码案例,展示了信号量在线程间和进程间同步的具体应用。全文最后总结了两种同步机制的选择原则和注意事项,为开发多线程/多进程程序提供了实用参考。

2302_77530416的博客 1693

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【Linux 内核】进程管理 ( 内核线程概念 | 内核线程、普通进程、用户线程 | 内核线程与普通进程区别 | 内核线程主要用途 | 内核线程创建函数 kernel_thread 源码 )

一、内核线程概念、 二、内核线程、普通进程、用户线程、 三、内核线程、普通进程区别、 四、内核线程主要用途、 五、内核线程创建函数 kernel_thread 源码、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 2752

Linux---线程---全面讲解

Linux线程作为轻量级执行单元,在系统编程中扮演着关键角色。本文系统讲解了线程基础概念、核心API、同步机制和高级特性。重点内容包括:线程与进程的本质区别,Linux通过轻量级进程实现线程;pthread库的核心操作如创建、等待、终止和分离线程;多种同步机制(互斥锁、条件变量、读写锁等)的原理与使用场景;线程局部存储、线程安全等高级特性;以及死锁、线程泄漏等常见问题的解决方案。文章结合代码示例和底层实现细节,帮助开发者深入理解Linux线程的运行机制和最佳实践。

我的博客主要是用来给我自己复习的,主要是上传一些知识点,小型的项目和功能模块,如果有什么问题或错误麻烦联系我! 1815

linux----线程

同步方案流程中,等待队列负责对需访问共享资源的执行流进行排队:先通过锁管理资源访问,未获得锁的执行流进入等待队列阻塞,待持有锁的执行流释放锁后,按顺序唤醒队列中的下一个执行流获取锁,确保资源访问有序。而线程间代码的分离,关键在于让不同线程执行不同的函数——在C和C++中,所有变量和代码均使用虚拟地址,定义的多个函数会被编译器以平坦模式赋予不同的线性编址,即每个函数拥有唯一的虚拟地址,因此只需为每个线程指定不同的函数去执行,就能凭借函数地址的天然差异,在代码层面实现线程间的地址空间划分,达成代码分离。

2403_82706967的博客 845

【Linux】线程的控制

我们说Linux是用进程模拟的线程,所以Linux中只有轻量级进程的概念,但是,用户是只认线程的,所以我们有一个叫原生线程库的东西,它就负责把轻量级进程的系统调用进行封装,转成线程相关的接口提供给用户。为什么叫原生呢?就是说只要你装Linux系统,就必须装这个库,就是默认装好的库。有关线程的接口在3号手册,就证明它们不是系统调用,因为系统调用在2号手册。当然也有创建轻量级进程的系统调用,它是在二号手册我们可以说,其实pthread_create其实就是封装了这个系统调用。

2201_76024104的博客 831

面试知识点(操作系统)

个人记录,来源参考了很多网上的资料: 计算机网络常见面试知识点总结_松生的博客-优快云博客 文章目录计算机网络体系结构TCP三次握手四次挥手三次握手四次挥手为什么客户端最后还要等待2MSL如果已经建立了连接,但是客户端突然出现故障了怎么办?TCP,UDP协议,分析上层协议应该使用 UDP 还是 TCPTCP, UDP协议的区别TCP协议如何保证可靠传输ARQ协议:如何解决线程不安全操作锁有哪些类型?什么情况下可能发生死锁,怎么解决死锁。 死锁的四个条件和避免的方法死锁产生的原因:产生死锁的四个必要条件:处

zj 929

linux的进程/线程/协程系列1:进程到协程的演化

本文从批处理时代讲起,由批处理时代的问题引出进程,同时简述现代操作系统的启动,然后由进程问题依次引出线程、线程池、协程。 本篇主要是概念讲解,本来打算直接上命令代码,可仔细思考后,认为文字讲解对于理解进程到协程的来龙去脉还是必不可少的,急于操作的同学可以跳到第二篇。

shao918516的博客 2170

(八)Linux的进程与线程

多任务处理是指用户可以在同一时间内运行多个应用程序,每个正在执行的程序被称为一个任务。一个任务包含一个或多个完成独立功能的子任务,其中子任务可以是进程或线程。Linux就是一个支持多任务的操作系统,比起单任务系统它的功能增强许多。

小新的博客 984

Linux系统 - 线程 -3- 线程安全和每线程存储

要使用线程特有数据,库函数执行的一般步骤如下。函数创建一个键(key),用以将不同函数使用的线程特有数据项区分开来。调用函数pthread_key_create()可创建此“键”,且只需在首个调用该函数的线程中创建一次,函数pthread_once()的使用正是出于这一目的。键在创建时并未分配任何线程特有数据块。调用 pthread_key_create()还有另一个目的,即允许调用者指定一个自定义解构函数,用于释放为该键所分配的各个存储块(参见下一步)。

Android系统工程师--小馬佩德罗 199

Linux 相关博文链接和知识汇总

个人总结的Linux知识点,便于学习和复习 其中APUE详细介绍了Unix系统各个层次的知识 深入理解Linux内核也讲述了大量的Linux知识,两者相辅相成,结合着看一定大有收获 最后有关于Linux所有system call和library的列表,如open,close,read,write等。方便查阅各个函数的使用方法。 包含如下内容: Linux著作《APUE》的知识点

feather(猎羽) 984

线程池的实现和讲解:解决多线程并发服务器创建销毁线程消耗过大的问题

多线程并发服务器有一个问题:有多少个client就有多少个线程,CPU需要在多个线程之间来回切换处理客户端的请求,系统开销比较大(特别是销毁线程的时候),那么就可以采用线程池的方法,一次性创建一堆线程,放进线程池进行统一管理。

caiji0169的博客 2005
上一篇: Linux相关概念和易错知识点(28)(线程控制、Linux下线程的底层)
下一篇: Linux相关概念和易错知识点(30)(线程互斥、线程同步)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值