当前位置: 首页 > news >正文

Linux--线程的分离、线程库的地址关系的理解、线程的简单封装(二)

线程系列:
线程的认识:讲解线程的概念和线程的基本控制

线程的分离

线程分离是指将一个线程从主线程中分离出来,使其能够独立运行。当一个线程被设置为分离状态时,它结束时系统会自动回收其资源,而不需要其他线程使用pthread_join()函数来等待其结束并手动回收资源。

设置线程分离的方法
使用pthread_detach()函数:在线程创建后,可以通过调用pthread_detach()函数来将线程设置为分离状态。这个函数是非阻塞式的,即调用后不会阻塞当前线程的执行。
在创建线程时设置分离属性:另一种方法是在创建线程时,通过pthread_create()函数的第二个参数(线程属性)来设置线程为分离状态。这种方法在创建线程时即指定了其分离属性,效率相对较高。

void* threadrun(void* args)
{string name = static_cast<const char *>(args);while(true){sleep(1);cout<<"this is new thread:"<<name<<endl;}
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadrun, (void *)"thread 1");cout << "main thread wait block" << std::endl;pthread_join(tid, nullptr);cout << "main thread wait return"<<endl;
}

在这里插入图片描述

使用分离函数后:
在这里插入图片描述

再加个有限时间的循环看看:
在这里插入图片描述
对线程分离理解虽然新线程与主线程已经分离了,但它们仍然是同一进程中的执行流,如果程序使用时出现异常时(新线程或者主线程),那么两个程序都会终止;或者说主线程结束了,实际上就代表进程结束了;所以线程的分离仍然是在进程中进行的,受进程的影响

何时使用:当线程完成任务后不需要与其结果交付时;当线程在后台运行且不需要与主线程进行同步进行时;

注意:分离线程无法重新连接!而可连接线程可以分离,当只有在尚未开始运行之前

理解线程库的地址关系

在这里插入图片描述

在这里插入图片描述

线程栈

线程栈是与线程紧密相关的内存区域,用于存储线程的局部变量、函数调用的返回地址以及线程的执行上下文等信息每个线程都有自己独立的栈空间,这保证了线程之间的数据是隔离的,从而避免数据竞争和线程安全问题。

#include<iostream>
using namespace std;
#include<pthread.h>
#include<unistd.h>
void *threadrun1(void *args)
{  std::string name = static_cast<const char *>(args);int g_val=100;while(true){sleep(1);printf("%s, g_val: %lu, &g_val: %p\n", name.c_str(), g_val--, &g_val);}return nullptr;
}void *threadrun2(void *args)
{std::string name = static_cast<const char *>(args);int g_val=100;while(true){printf("%s, g_val: %lu, &g_val: %p\n", name.c_str(), g_val--, &g_val);sleep(1);}return nullptr;
}int main()
{pthread_t tid1;pthread_t tid2;pthread_create(&tid1, nullptr, threadrun1, (void *)"thread 1");pthread_create(&tid2, nullptr, threadrun2, (void *)"thread 2");pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);
}

通过两个新线程都创建一个局部变量(变量名相同),比较它们的地址;
在这里插入图片描述
可以看到g_val在各自线程是不一样的,地址也是不同的;

线程局部存储(TLS)

线程局部存储(TLS)是一种机制,允许每个线程拥有自己的私有数据副本,即使不同线程执行相同的代码,TLS变量与常规全局变量是不同的,因为每个线程堆TLS变量的访问都是独立的。

一般适用于:

  • 线程特定数据:当某些数据只对特定线程有意义,并且需要在线程内保持状态时,可以使用线程局部存储。
  • 全局状态隔离:通过将全局状态分离为每个线程的私有副本,可以提高并发性能,避免线程间的数据竞争。
  • 线程上下文保存:线程局部存储也可用于保存当前执行线程的上下文信息,如用户身份验证信息、数据库连接等。

注意:

线程局部存储变量通常只能用于具有静态或线程存储期的变量,不能用于自动或动态分配的变量。使用线程局部存储时需要谨慎管理内存,避免内存泄漏或无效访问等问题。

线程的封装

线程的封装通常指的是将线程的创建、执行、同步、资源管理等逻辑封装到一个类或对象中,以便更好地组织代码,提高代码的可读性和可维护性

封装线程可以隐藏线程的复杂性,使得其他部分的代码可以更加简洁地与线程进行交互。

下面看具体代码:

Thread.hpp:对线程的封装

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include<iostream>
#include<string>
#include<pthread.h>
#include<functional>
#include<unistd.h>using namespace std;namespace ThreadMdule
{//通过模板类可调用一切任何对象template<typename T>using func_t = std::function<void(T&)>;template<typename T>class Thread{public:void Excute(){_func(_data);}Thread(func_t<T> func, T data, const std::string &name="none-name"): _func(func), _data(data), _threadname(name), _stop(true){}static void* threadroutine(void* args){Thread<T>* self=static_cast<Thread<T>*>(args);self->Excute();return nullptr;}bool start(){int n=pthread_create(&_tid,nullptr,threadroutine,this);if(!n){_stop = false;return true;}else{return false;}}void Detach(){if(!_stop){pthread_detach(_tid);}}void Join(){if(!_stop){pthread_join(_tid,nullptr);}}string name(){return _threadname;}void Stop(){_stop = true;}~Thread() {}private:pthread_t _tid;std::string _threadname;T _data;  func_t<T> _func;bool _stop;};
}#endif

线程类中包括了:线程名,数据,调用函数指针等;
通过start()函数来创建新线程:用到了函数threadroutinue,在函数中将函数成员_func(也就是具体函数的指针)使用了起来,就表示新线程的创建使用;

主函数的调用:

void print(int &cnt)
{while (cnt){std::cout << "hello I am myself thread, cnt: " << cnt-- << std::endl;sleep(1);}
}const int num=3;
int main()
{vector<Thread<int>> threads;//创建新线程for(int i=0;i<num;i++){string name="thread"+to_string(i + 1);threads.emplace_back(print,3,name);}//启动进程for(auto& thread:threads){thread.start();}//等待进程结束for(auto& thread:threads){thread.Join();cout<<"wait thread done,thread is: "<<thread.name()<<endl;}return 0;
}

在这里插入图片描述
这样就是对线程的简单封装;

通过封装线程,我们可以更好地控制线程的创建、执行和销毁过程,同时使得代码更加清晰和易于维护。

此外,封装还可以帮助我们添加额外的功能,比如线程池的集成、异常处理、线程同步等。

相关文章:

  • Kubernetes 之 Secret
  • App开发前端开发语言:深度解析与应用探索
  • MySQL—函数—函数小结
  • 民国漫画杂志《时代漫画》第33期.PDF
  • 必看——怎么让网站实现HTTPS访问?
  • 用java实现客服聊天+网络爬虫下载音乐(java网络编程,io,多线程)
  • 安卓组合控件(底部标签栏、顶部导航栏、增强型列表、升级版翻页)
  • Java 内存模型
  • Java中的JDBC如何连接数据库并执行操作
  • Windows API 速查
  • 每日一题——Java编程练习题
  • Vue3集成Phaser-飞机大战游戏(设计与源码)
  • 基于深度学习的音乐合成算法实例
  • LangChain学习之四种Memory模式使用
  • 基于springboot+vue的医院信息管理系统
  • 【Linux系统编程】快速查找errno错误码信息
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【剑指offer】让抽象问题具体化
  • 【面试系列】之二:关于js原型
  • android图片蒙层
  • avalon2.2的VM生成过程
  • PAT A1120
  • Rancher-k8s加速安装文档
  • SSH 免密登录
  • Tornado学习笔记(1)
  • Vim 折腾记
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 订阅Forge Viewer所有的事件
  • 飞驰在Mesos的涡轮引擎上
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 王永庆:技术创新改变教育未来
  • 我有几个粽子,和一个故事
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • mysql面试题分组并合并列
  • ​比特币大跌的 2 个原因
  • ‌‌雅诗兰黛、‌‌兰蔻等美妆大品牌的营销策略是什么?
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #微信小程序:微信小程序常见的配置传旨
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (1)(1.11) SiK Radio v2(一)
  • (2)Java 简介
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (Matlab)使用竞争神经网络实现数据聚类
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (二)WCF的Binding模型
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (六)c52学习之旅-独立按键
  • (四) Graphivz 颜色选择