【Linux系统编程】第四十四弹---从TID到线程封装:全面掌握线程管理的核心技巧

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、tid是什么

1.1、理解库 

1.2、理解tid

1.3、tid中线程局部存储

2、封装线程 

2.1、基本结构

2.2、函数实现

2.3、使用单线程

2.4、使用多线程


1、tid是什么

从前面的讲解我们猜测tid是一个虚拟地址,并使用代码打印出来,此处需要更进一步分析tid,先代码演示一下!!!

用到的头文件

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

 代码演示

std::string ToHex(pthread_t tid)
{
    char id[128];
    snprintf(id,sizeof(id),"0x%lx",tid);
    return id;
}

void* threadrun(void* args)
{
    std::string name = static_cast<const char*>(args);
    while(true)
    {
        // std::cout << name << " is running...,tid: " << pthread_self() << std::endl; 
        std::cout << name << " is running...,tid: " << ToHex(pthread_self()) << std::endl; 
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,threadrun,(void*)"thread-1");
    // std::cout << "new thread tid: " << tid << std::endl;
    std::cout << "new thread tid: " << ToHex(tid) << std::endl;

    pthread_join(tid,nullptr);
    return 0;
}

运行结果 

1.1、理解库 

 讲解tid之前我们需要先理解一下库

1、库是一个文件,保存在磁盘中,然后将库加载到内存,再通过页表将库映射到地址空间中

2、创建线程,前提是把库加载到内存,映射到进程的内存空间

3、创建线程,调用线程库中的系统调用函数

1.2、理解tid

库是如何做到对线程进行管理的呢?

先描述(库中创建描述线程的相关结构体字段属性),在组织("数组")。

如何理解这个虚拟地址?

可以类比C语言打开文件,通过地址找到文件!!!

tid是什么? 

线程属性集合的起始虚拟地址--- 在pthread库中维护

1.3、tid中线程局部存储

编写C++代码

int gval = 100;

std::string ToHex(pthread_t tid)
{
    char id[128];
    snprintf(id, sizeof(id), "0x%lx", tid);
    return id;
}

void *threadrun(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        std::string id = ToHex(pthread_self());
        std::cout << name << " is running...,tid: " << id << ",gval: " << gval << ",&gval: " << &gval << std::endl;
        sleep(1);
        gval++;
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, (void *)"thread-1");
    while (true)
    {
        std::cout << "main thread,gval: " << gval << ",&gval: " << &gval << std::endl;
        sleep(1);
    }
    pthread_join(tid, nullptr);
    return 0;
}

运行结果  

 如果想要每个线程使用独立的内存空间呢?

__thread (两个下划线)修饰全局变量,即在线程局部存储中开辟空间!!!

  • 定义:__thread是GCC内置的线程局部存储设施,用于创建线程局部变量
  • 作用保证每个线程拥有独立的变量副本,各个线程对该变量的操作互不干扰。
// Linux有效, __thread只能修饰内置类型
__thread int gval = 100;

 运行结果 

2、封装线程 

2.1、基本结构

线程的封装可以使用命名空间域,类成员变量有name,tid,isrunning,func!!!

namespace ThreadMoudle
{
    // 线程要执行的方法
    typedef void (*func_t)(const std::string& name); // 函数指针类型

    class Thread
    {
    public:
        // 执行回调函数
        void Excute();
    public:
        Thread(const std::string& name,func_t func):_name(name),_func(func);
        // 新线程执行该方法
        static void* ThreadRoutine(void* args);
        // 线程状态
        std::string Status();
        // 启动线程
        bool Start();
        // 停止线程
        void Stop();
        // 线程名字
        std::string Name();
        // 等待线程
        void Join();
        ~Thread();
    private:
        std::string _name; // 线程名
        pthread_t _tid; // 线程tid
        bool _isrunning; // 线程状态
        func_t _func; // 线程要执行的回调函数
    };
}

2.2、函数实现

构造函数

打印一条语句即可!

Thread(const std::string& name,func_t func):_name(name),_func(func)
{
    std::cout << "create " << _name << " done" << std::endl;
}

启动线程

启动线程的本质是创建一个新线程,创建成功返回true,创建失败返回false。

1、创建新线程需要调用执行函数,但是成员函数会有隐含的this指针,直接调用成员函数会报错,因为执行函数只能有一个参数,解决办法是使用静态成员函数,该函数属于类,不属于对象,因此没有隐含的this指针。

2、使用静态成员函数之后,没有this指针,就不能直接调用类对象的方法,此处的解决办法是将this指针传给执行函数。

void Excute()
{
    std::cout << _name << " is running" << std::endl;
    _isrunning = true;
    _func(_name);
    _isrunning = false;
}
// 新线程执行该方法
static void* ThreadRoutine(void* args)
{
    // _func(); // static 不能直接访问成员函数,没有this,传this指针
    Thread* self = static_cast<Thread*>(args);
    self->Excute();
    return nullptr;
}
bool Start()
{
    // ::使用库函数接口,直接使用ThreadRoutine会报错,因为成员函数有隐含this指针 + static
    int n = ::pthread_create(&_tid,nullptr,ThreadRoutine,this);
    if(n != 0) return false;
    return true;
}

停止线程

如果线程处于运行状态则需要停止线程,停止的本质是取消线程,修改线程运行状态即可!

void Stop()
{
    if(_isrunning)
    {
        ::pthread_cancel(_tid);
        _isrunning = false;
        std::cout << _name << " Stop" << std::endl;
    }
}

等待线程

 等待线程即调用等待线程的系统调用即可!

void Join()
{
    ::pthread_join(_tid,nullptr);
    std::cout << _name << " Join" << std::endl;
}

其他函数 

std::string Status()
{
    if(_isrunning)
        return "running";
    else 
        return "sleep";
}
std::string Name()
{
    return _name;
}
~Thread()
{}

2.3、使用单线程

自己管理单线程!! 先描述在组织!!!

void Print(const std::string &name)
{
    int cnt = 1;
    while (true)
    {
        std::cout << name << " is running,cnt: " << cnt++ << std::endl;
        sleep(1);
    }
}

int main()
{
    Thread t("thread-1", Print);
    t.Start();
    sleep(1); // 等待一秒,因为线程执行顺序不确定,可能主线程后执行
    std::cout << t.Name() << ",status: " << t.Status() << std::endl;
    sleep(10);

    t.Stop();
    sleep(1);
    std::cout << t.Name() << ",status: " << t.Status() << std::endl;

    t.Join();
    std::cout << t.Name() << ",status: " << t.Status() << std::endl;
    return 0;
}

 

2.4、使用多线程

要使用多线程,实质就是创建多个线程,然后将多线程管理起来,我们可以使用vector容器!

新线程执行函数 

void Print(const std::string &name)
{
    int cnt = 1;
    while (true)
    {
        std::cout << name << " is running,cnt: " << cnt++ << std::endl;
        sleep(1);
    }
}

主函数 

const int gnum = 10;

int main()
{
    std::vector<Thread> threads;
    // 创建线程
    for(int i=0;i<gnum;i++)
    {
        std::string name = "thread" + std::to_string(i+1);
        threads.emplace_back(name,Print);
        sleep(1);
    }

    // 统一启动
    for(auto& thread : threads)
    {
        thread.Start();
    }

    sleep(3);

    // 统一停止
    for(auto& thread : threads)
    {
        thread.Stop();
    }

    // 统一等待
    for(auto& thread : threads)
    {
        thread.Join();
    }
    return 0;
}

运行结果 

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

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

相关文章

医学图像算法之基于Unet的视网膜血管分割

第一步&#xff1a;准备数据 视网膜血管分割数据比较少&#xff0c;但效果好&#xff0c;总共40张 第二步&#xff1a;搭建模型 UNet主要贡献是在U型结构上&#xff0c;该结构可以使它使用更少的训练图片的同时&#xff0c;且分割的准确度也不会差&#xff0c;UNet的网络结构…

ARM死机(HardFault)调试技巧详解(栈回溯,不破坏现场)

目录 Keil调试技巧&#xff1a; 一.不破坏现场连接仿真器与进入debug 二.栈回溯 死机调试示例 J-Link调试方法 示例&#xff1a;空指针异常 不能连接烧录器或者读取内存怎么办&#xff1f; 在日常开发中&#xff0c;经常会遇到单片机卡死机等问题&#xff0c;经常很难定…

nodejs 020: React语法规则 props和state

props和state 在 React 中&#xff0c;props 和 state 是管理数据流的两种核心机制。理解它们之间的区别和用途是构建 React 应用程序的基础。 一、props 和 state的区别 特性propsstate定义方式由父组件传递给子组件的数据组件内部管理的本地数据是否可修改不可变&#xff…

【开源免费】基于SpringBoot+Vue.JS水果购物网站(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 065 &#xff0c;文末自助获取源码 \color{red}{T065&#xff0c;文末自助获取源码} T065&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

python可视化进阶

引用&#xff1a; 首先需要安装 plotnine from plotnine import* import joypy数据可视化进阶操作 3.1 类别数据可视化 【例3-1】——绘制简单条形图 【代码框3-1】——绘制简单条形图 # 图3-1的绘制代码 import pandas as pd import matplotlib.pyplot as plt from cvxpy …

大模型入门自学资源汇总,很难找到比这还全的大模型学习资源总结了!

接触各种AI工具到现在也快两年了&#xff0c;今年和同学陆续做了一些AI应用的科普宣讲&#xff0c;在这过程中收集了不少自学资源&#xff0c;特地挑出一部分整理成以下的内容。 书籍 大模型应用开发极简入门&#xff1a;基于GPT-4和ChatGPT 首推今年年初出版的《大模型应用开…

为何选择Spring AI Alibaba开发智能客服平台?

0 前言 本文来看如何使用Spring AI Alibaba构建Agent应用。 1 需求 智能客服平台&#xff0c;可帮助用户完成机票预定、问题解答、机票改签、取消等动作&#xff0c;具体要求&#xff1a; 基于 AI 大模型与用户对话&#xff0c;理解用户自然语言表达的需求支持多轮连续对话…

Python学习从0到1 day27 第三阶段 Spark ② 数据计算Ⅰ

人总是会执着于失去的&#xff0c;而又不珍惜现在所拥有的 —— 24.11.9 一、map方法 PySpark的数据计算&#xff0c;都是基于RDD对象来进行的&#xff0c;采用依赖进行&#xff0c;RDD对象内置丰富的成员方法&#xff08;算子&#xff09; map算子 功能&#xff1a;map算子…

数据结构合并两个有序链表

数据结构 1.合并两个有序数组代码&#xff1a; 1.合并两个有序数组 这里我们可以创建一个新的对象作为合并后的新链表newHead&#xff0c;而NewHead.next就是我们要返回的头部的位置 在创建一个对象来获取nextHead.next下一个节点来作为我们新链表的起始位置防止我们的头部位置…

动态规划 —— dp 问题-买卖股票的最佳时机含手续费

1. 买卖股票的最佳时机含手续费 题目链接&#xff1a; 714. 买卖股票的最佳时机含手续费 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/ 2. 算法原理 状态表示&#xff1a;以某一个…

利用pythonstudio写的PDF、图片批量水印生成器,可同时为不同读者生成多组水印

现在很多场合需要将PDF或图片加水印&#xff0c;本程序利用pythonstudio编写。 第一步 界面 其中&#xff1a; LstMask:列表框 PopupMenu:PmnMark LstFiles:列表框 PopupMenu:PmnFiles OdFiles:文件选择器 Filter:PDF文件(.PDF)|.PDF|图像文件(.JPG)|.JPG|图像文件(.png…

基于python深度学习技术矩阵分解的推荐系统,通过学习隐含特征,实现推荐

实现了一个基于矩阵分解的推荐系统&#xff0c;用于预测用户对电影的评分。具体来说&#xff0c;该程序通过TensorFlow构建和训练一个模型&#xff0c;来学习用户和电影之间的隐含特征&#xff0c;并根据这些特征预测评分。以下是代码的主要功能和步骤的详细描述&#xff1a; …

[vulnhub] DarkHole: 1

https://www.vulnhub.com/entry/darkhole-1,724/ 端口扫描主机发现 探测存活主机&#xff0c;184是靶机 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-08 09:59 CST Nmap scan report for 192.168.75.1 Host is up (0.00027s latency). MA…

4.1 WINDOWS XP,ReactOS对象与对象目录----1

系列文章目录 文章目录 系列文章目录4.1 对象与对象目录OBJECT_HEADERObpLookupEntryDirectory()NtCreateTimer() 4.1 对象与对象目录 “对象(Object)”这个词现在大家都已耳熟能详了&#xff0c;但是对象到底是什么呢?广义地说&#xff0c;对象就是“目标”&#xff0c;行为…

STM32H503开发(2)----STM32CubeProgrammer烧录

STM32H503开发----2.STM32CubeProgrammer烧录 概述硬件准备视频教学样品申请源码下载参考程序自举模式BOOT0设置UART烧录USB烧录 概述 STM32CubeProgrammer (STM32CubeProg) 是一款用于编程STM32产品的全功能多操作系统软件工具。 它提供了一个易用高效的环境&#xff0c;通过…

“双十一”电商狂欢进行时,在AI的加持下看网易云信IM、RTC如何助力商家!

作为一年一度的消费盛会&#xff0c;2024年“双十一”购物狂欢节早已拉开帷幕。蹲守直播间、在主播热情介绍中点开链接并加购&#xff0c;也已成为大多数人打开“双11”的重要方式。然而&#xff0c;在这火热的购物氛围背后&#xff0c;主播频频“翻车”、优质主播稀缺、客服响…

debian系统安装qt的时候 显示xcb相关文件缺失

如果是安装之后的问题 我们可以选择使用ldd的命令查看当前依赖的so那些文件确实 ldd /home/yinsir/Qt/5.15.2/gcc_64/plugins/platforms/libqxcb.so 本人在进行打包的时候 出现则会个报错 ERROR: ldd outputLine: “libxcb-util.so.1 > not found” ERROR: for binary: “/…

A023-基于SpringBoot的冷链物流系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

【数据分析】如何构建指标体系?

有哪些指标体系搭建模型&#xff1f;五个步骤教你从0开始搭建指标体系 一、企业指标体系搭建存在什么问题 许多企业在搭建数据指标体系时遇到了诸多难题&#xff0c;如问题定位不准确、数据采集不完整、目标不一致、报表无序、指标覆盖不全面以及报表价值未充分利用等。 1、…

C++20 概念与约束(1)—— SFINAE

1、从模板说起 众所周知&#xff0c;C在使用模板时&#xff0c;如果有多个模板匹配&#xff0c;则编译器会选择最匹配的一个模板进行实例化&#xff0c;这也正是模板特化和偏特化的依据。 根据上面这张图中的现象&#xff0c;列举下面几个示例&#xff1a; 1、不存在模板的情况…