【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; // 线程tidbool _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指针 + staticint 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;
}

运行结果 

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

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

相关文章

WPF 打包

打包为单个exe文件直接运行 - - -版本.NET8 新建WPF项目 右键 - 发布 选择发布文件夹 选择发布文件夹 选择发布文件夹 配置 配置,保存 发布 WPF 打包为exe安装程序 示例 实现思路 引导项目中嵌入其它项目可运行目录的zip引导项目中解压zip文件到指定文件夹是…

percona tpc-c程序压测mysql8.0并绘图

1 概述 OLTP数据库选型一般会从稳定性、性能、易用性、官方文档等因素考虑。而在性能这因素&#xff0c;基准测试有sysbench和tpc-c&#xff08;tpc-e是tpc-c的升级版&#xff09;。sysbench的底层测试用例的表结构是简单的&#xff0c;并且不支持join&#xff0c;针对以上两个…

ArcGIS软件之“计算面积几何”地图制作

目录 一、消防站的泰森多边形ex12二、人口调查的泰森多边形三、人口调查的泰森多边形属性设置四、计算面积几何,用于求密度五、求密度六、给“现有中学”属性 R1赋值七、“现有中学”设置多环缓存区 并为它赋值八、“土地使用”为不同的功能区赋值九、三个图层相交十、计算面积…

【Web前端】使用 JSON 处理数据

JSON 是一种基于 JavaScript 对象语法的数据格式&#xff0c;由道格拉斯克罗克福特推广。尽管其语法源于 JavaScript&#xff0c;JSON 仍然是独立于 JavaScript 的&#xff0c;这也是为什么许多编程环境能够解析和生成 JSON 的原因。JSON 可以以对象或字符串的形式存在&#xf…

SMOTE算法深度解析及代码实现

SMOTE算法介绍 SMOTE算法是较为常用的数据增广算法&#xff0c;其核心思路是在少数类别样本内部进行数据合成&#xff0c;更具体的说&#xff0c;其后隐藏的猜想是假定样本 x 0 , x 1 , . . , x N x_0,x_1,..,x_N x0​,x1​,..,xN​都为同一类别&#xff0c;那么他们的线性组合…

基于Redis缓存机制实现高并发接口调试

创建接口 这里使用的是阿里云提供的接口服务直接做的测试&#xff0c;接口地址 curl http://localhost:8080/initData?tokenAppWithRedis 这里主要通过参数cacheFirstfalse和true来区分是否走缓存&#xff0c;正常的业务机制可能是通过后台代码逻辑自行控制的&#xff0c;这…

鸿蒙系统:智能生态的新纪元与开发者的新机遇

正文&#xff1a; 在数字化时代&#xff0c;操作系统作为智能设备的灵魂&#xff0c;其重要性不言而喻。随着技术的不断进步&#xff0c;我们见证了安卓和iOS在全球范围内的广泛应用和影响力。如今&#xff0c;鸿蒙系统&#xff08;HarmonyOS&#xff09;以其创新的分布式架构…

Netty入门二

文章目录 EventLoopChannelFuture 与 PromiseHandler与PipelineByteBuf Netty的核心组件包括以下几种&#xff1a; EventLoop&#xff1a;负责处理注册到其上的channel的所有I/O事件。Channel&#xff1a;表示数据传输的网络通道。Future 与 Promise&#xff1a;Future用于等待…

一文读懂剪枝(Pruner):大模型也需要“减减肥”?

当你听到「剪枝」二字&#xff0c;或许会联想到园丁修整枝叶的情景。而在 AI 大模型领域&#xff0c;这个词有着特殊的含义 —— 它是一种通过“精简”来提升大模型效率的关键技术。随着 GPT、LLaMA 等大模型规模的持续膨胀&#xff0c;如何在保持性能的同时降低资源消耗&#…

单词反转和数组去重,附经典面试题一份

博彦科技笔试&#xff1a; 给定字符&#xff0c;拼接成单词进行反转单词&#xff1b; package org.example;public class Main {public static void main(String[] args) {char[] input {h, e, l, l, o, , w, o, r, l, d, , J, a, v, a};String inputToString new String(…

【51单片机】UART串口通信原理 + 使用

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 串口硬件电路UART串口相关寄存器 编码单片机通过串口发送数据电脑通过串口发送数据控制LED灯 串口 串口是一种应用十分广泛的通讯接…

构建智能防线 灵途科技光电感知助力轨交全向安全防护

10月27日&#xff0c;在南京南站至紫金山东站间的高铁联络线上&#xff0c;一头野猪侵入轨道&#xff0c;与D5515次列车相撞&#xff0c;导致设备故障停车。 事故不仅造成南京南站部分列车晚点&#xff0c;还在故障排查过程中导致随车机械师因被邻线限速通过的列车碰撞而不幸身…

不使用递归的决策树生成算法

不使用递归的决策树生成算法 利用队列 queue &#xff0c;实现层次遍历&#xff08;广度优先遍历&#xff09;&#xff0c;逐步处理每个节点来建立子树结构。再构建一个辅助队列&#xff0c;将每个节点存储到 nodes_to_process 列表中&#xff0c;以便在树生成完成后可以反向遍…

自动驾驶系列—自动驾驶如何实现厘米级定位?深入解读GPS/RTK技术与应用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

DevOps业务价值流:需求设计最佳实践

DevOps实践正推动着产品快速迭代与高质量交付&#xff0c;但需求设计作为产品开发的关键起点&#xff0c;往往被忽视。它不仅是收集与分析需求的过程&#xff0c;更是将需求转化为可实施产品特性的核心。本文深入探讨DevOps业务价值流中的需求设计&#xff0c;从调研、整理、原…

【MySQL】数据库整合攻略 :表操作技巧与详解

前言&#xff1a;本节内容讲述表的操作&#xff0c; 对表结构的操作。 是对表结构中的字段的增删查改以及表本身的创建以及删除。 ps&#xff1a;本节内容本节内容适合安装了MySQL的友友们进行观看&#xff0c; 实操更有利于记住哦。 目录 创建表 查看表结构 修改表结构 …

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 …

使用 GitHub Actions 部署到开发服务器的详细指南

使用 GitHub Actions 部署到开发服务器的详细指南 在本篇博客中&#xff0c;我们将介绍如何使用 GitHub Actions 实现自动化部署&#xff0c;将代码从 GitHub 仓库的 dev 分支自动部署到开发服务器。通过这种方式&#xff0c;可以确保每次在 dev 分支推送代码时&#xff0c;服…

冒泡选择法(c基础)

适合对象c语言初学者。 冒泡选择法 作用对一个数组进行排序。&#xff08;介绍一下数组(c基础)(详细版)-CSDN博客&#xff09; 核心要点 1: 数组元素个数 sz 2: 比较后的交换。 核心思路 进行&#xff08;sz - 1&#xff09;趟&#xff0c;每一趟把最大数的放到末尾。其…

量子计算及其在密码学中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 量子计算及其在密码学中的应用 量子计算及其在密码学中的应用 量子计算及其在密码学中的应用 引言 量子计算概述 定义与原理 发展…