Linux线程管理进阶:分离,等待、终止与C++11线程接口的封装实践

🍑个人主页:Jupiter.
🚀 所属专栏:Linux从入门到进阶
欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述

目录

      • `🍑线程终止`
      • `🍍线程等待`
            • *多线程创建,传自己定义的对象示例代码:*
    • `🍎线程的分离`
      • `🍌对C++11中线程的代码实现`


🍑线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
  • 其中,主线程退出==进程退出,该进程的所有线程全部退出。
  • 其中,不能调用exit来终止线程,因为任何一个线程调用exit都会使整个进程退出。

注意:多线程中,任何一个线程出了异常,都会导致整个进程退出。— 多线程代码往往健壮性不好

pthread_exit函数

  • 功能:线程终止
  • 原型
    • void pthread_exit(void *value_ptr);
  • 参数
    • value_ptr:value_ptr不要指向一个局部变量。
    • 返回值:无返回值
      需要注意:调用pthread_exit后,类似return ,即 pthread_exit((void*)100) == return (void *)100 ,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

pthread_cancel函数

  • 功能:取消一个执行中的线程
  • 原型
    • int pthread_cancel(pthread_t thread);
  • 参数
    • thread:线程ID
    • 返回值:成功返回0;失败返回错误码

🍍线程等待

已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
创建新的线程不会复用刚才退出线程的地址空间。

  • 功能:等待线程结束
  • 原型
    • int pthread_join(pthread_t thread, void **value_ptr);
  • 参数
    • thread:线程ID
    • value_ptr:它指向一个指针,后者指向线程的返回值
    • 返回值:成功返回0;失败返回错误码

调用该函数的线程将挂起等待,直到id为thread的线程终止(阻塞等待)。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  • 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  • 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED((void *) -1)
  • 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  • 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
  • 注意:pthread_join,不考虑出现异常的情况,因为线程异常,整个进程都结束了,主线程没有pthread_join的机会。

示例代码:

std::string ToHex(pthread_t tid)   //将id转换为十六进制
{char buff[64];snprintf(buff, 64, "0x%lx", tid);return buff;
}void *routine(void *name)
{int cnt = 5;while (cnt--){sleep(1);std::cout << "i am new thread,name:" << (char *)name << "  my tid is :" << ToHex(pthread_self()) << std::endl;}returnvoid*100;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)("thread-1"));void *ret = nullptr;int n = pthread_join(tid,&ret);std::cout<<"new thread exit,n = "<< n << "get a return val:"<<(lont lont)ret<<std::endl;return 0;
}

在这里插入图片描述

多线程创建,传自己定义的对象示例代码:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <vector>#define threadnum 5// 线程执行的任务
class Task
{
public:Task(int x = 10, int y = 15) : _x(x), _y(y){}int excute(){return _x + _y;}~Task(){}
private:int _x;int _y;
};// 存储线程执行结果
class Result
{
public:Result(int result, std::string name) : _result(result), _name(name){}void Print(){std::cout << _name << " Task result is " << _result << std::endl;}~Result(){}
private:int _result;std::string _name;
};// 存储线程香相关数据
class threadDate : public Task
{
public:threadDate(int x, int y, const std::string &name) : _name(name), _t(Task(x, y)){}std::string name(){return _name;}int Result(){return _t.excute();}~threadDate(){}
private:std::string _name;Task _t;
};// 线程执行的函数
void *threadrun(void *argc)
{threadDate *td = static_cast<threadDate *>(argc);sleep(1);Result *Res = new Result(td->Result(), td->name());delete td;return Res;
}int main()
{// 1.创建多线程std::vector<pthread_t> threads;for (int i = 0; i < threadnum; i++){char *name = new char[64];snprintf(name, 64, "thread-%d", i + 1);// 2.参数设置threadDate *td = new threadDate(10, 15, name);pthread_t tid;pthread_create(&tid, nullptr, threadrun, td);threads.push_back(tid);      //将线程的tid存放到一个vector中,便于等待}// std::vector<Result *> threadRes;   //将结果的结构体放到vector中void *Res = nullptr;// 等待线程,并取task结果for (auto &tid : threads){pthread_join(tid, &Res);((Result *)Res)->Print();// threadRes.push_back((Result *)Res);  //push_back进vector}// 打印结果//  for (auto &Res : threadRes)//  {//      Res->Print();//  }return 0;
}

🍎线程的分离

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

int pthread_detach(pthread_t thread);

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:

pthread_detach(pthread_self());

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

注意:如果你尝试对已经分离的线程(包括通过 pthread_self() 获取的当前线程)调用 pthread_join(),通常会导致未定义行为,但在大多数实现中,它会立即返回一个错误,通常是 EINVAL(表示无效参数)。分离只是不需要等待,底层依旧属于同一个进程

示例代码:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cerrno>void *threadrun(void *args){//pthread_detach(pthread_self()); // 将pthread_self()线程与其他线程分离开std::string name = static_cast<char *>(args);int cnt = 5;while (cnt--){std::cout << "i am " << name << std::endl;sleep(1);}return nullptr;
}int main() {pthread_t tid;pthread_create(&tid, nullptr, threadrun, (void *)"thread -1");pthread_detach(tid);   //将指定tid的线程分离int n = pthread_join(tid, nullptr);std::cout << "main thread wait return:" << n << ":" << strerror(n) << std::endl;return 0;
}

🍌对C++11中线程的代码实现

理解下面代码threadrun函数需要设置为static的原因,以及传入this的原因。

  • 因为pthread_create(),要求传入的函数参数为void *,如果不将函数设置为static,则参数会有一个this指针,所以需要设置static,那为什么要传入this指针呢?因为static成员函数没有this指针,但是func() 的调用需要this指针;
#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include<iostream>
#include<unistd.h>
#include<functional>
#include<string>namespace ThreadModule
{template<class T>using func_t = std::function<void(T&)>;// typedef std::function<void(const T&)> func_t;template<class T>class Thread{public:Thread(func_t<T> func,T& data,std::string name):_func(func),_data(data),_name(name),_stop(true){}void execute(){_func(_data);}static void *threadrun(void *args){Thread * self = static_cast<Thread*>(args);self->execute();return nullptr;}bool start(){int n = pthread_create(&_tid,nullptr,threadrun,this);  if(!n){_stop=false;return true;}else{return false;}}void stop(){if(!_stop)_stop=true;}void detach(){if(!_stop)pthread_detach(_tid);}std::string &name(){return _name;}void join(){if(!_stop)pthread_join(_tid,nullptr);}~Thread(){}private:pthread_t _tid;func_t<T> _func;T& _data;std::string _name;bool _stop;};
}#endif

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

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

相关文章

STM32(十二):DMA直接存储器存取

DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#xff0c;节省了CPU的资源。&#xff08;运行内存SRAM、程序存储器Flash、寄存器&#xff09; 12个独立可配置的通道&…

[数据集][目标检测]井盖丢失未盖破损检测数据集VOC+YOLO格式2890张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2890 标注数量(xml文件个数)&#xff1a;2890 标注数量(txt文件个数)&#xff1a;2890 标注…

页面要突破手机安全区域来全屏显示(沉浸式模式显示),其他页面不需要,如何设置安全区域文字颜色

#效果图 ##思路遇到的问题 在aboutToAppear中使用window模块的 getLastWindow 和 setWindowLayoutFullScreen两个方法来处理全屏显示 设置沉浸式模式的特点&#xff1a; 在任何一个页面中设置过一次之后&#xff0c;其他页面也会跟着全屏显示 这么处理会出现问题&#xff1a…

如何使用Spoon连接data-integration-server并在服务器上执行转换

1.建立连接 2.新建转换或任务 3.右键[子服务器]&#xff0c;新建一个服务器连接(data-integration-server服务器的连接信息) 4.右键[Run configurations],新建一个执行连接,勾选相应的选项即可: 5.选择服务器运行即可! 6.最后&#xff0c;你可以通过服务器端的WEB查看执行日志…

猫眼电影字体破解(图片转码方法)

问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后&#xff0c;数据全是加密后的。所以我们需要想办法破解加密&#xff0c;拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的&#xff…

【MySQL】MySQL操作介绍

MySQL操作 认识 MySQL什么是 MySQL关系型数据库的组成结构"客户端-服务器"结构 数据库的基本操作创建数据库查看数据库删除数据库使用数据库 数据类型整型浮点类型字符串类型日期类型总结 表的操作创建表查看表查看表的信息删除表 数据的基础操作插入数据指定列插入全…

Java过滤器和监听器

1. 过滤器 1.1. 过滤器 使用baseServlet的优点&#xff1a;方便进行乱码的统一处理&#xff0c;但是如果不使用servlet&#xff0c;在进行中文乱码处理时就需要在每个servlet中书写&#xff0c;比较麻烦 问题&#xff1a;重复的代码在项目中多次使用书写 解决&#xff1a;过滤…

2024 第七届“巅峰极客”网络安全技能挑战赛初赛 Web方向 题解WirteUp

EncirclingGame 题目描述&#xff1a;A simple game, enjoy it and get the flag when you complete it. 开题&#xff0c;前端小游戏&#xff0c;红点出不去就行 直接玩通关了 看看如何不玩也能拿到flag&#xff0c;flag存储在后端php文件内&#xff0c;前端找不到。 看一下…

【原创】java+springboot+mysql校园二手商品交易网设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

【MySQL】MySQL基础

目录 什么是数据库主流数据库基本使用MySQL的安装连接服务器服务器、数据库、表关系使用案例数据逻辑存储 MySQL的架构SQL分类什么是存储引擎 什么是数据库 mysql它是数据库服务的客户端mysqld它是数据库服务的服务器端mysql本质&#xff1a;基于C&#xff08;mysql&#xff09…

QT 编译报错:C3861: ‘tr‘ identifier not found

问题&#xff1a; QT 编译报错&#xff1a;C3861&#xff1a; ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中&#xff0c; 解决方案 就直接用类名引用 &#xff1a;QObject::tr( )

【舍入,取整,取小数,取余数丨Excel 函数】

数学函数 1、Round函数 Roundup函数 Rounddown函数 取整&#xff1a;(Int /Trunc)其他舍入函数&#xff1a; 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字&#xff0c;保留几位小数)&#xff08;四…

SOEX从去中心化的链上社交关系到创收策略

是时候摆脱传统的在线社区&#xff0c;真正进入 Web3 了&#xff0c;利用区块链的力量&#xff0c;并理解社交互动的意义远不止分享内容或复制交易。代币化将赋能参与提升到一个全新的水平&#xff0c;并带来一系列新的机会。 社交网络可以发挥强大的作用&#xff0c;尤其是从…

Android的logcat日志详解

Android log系统 logcat介绍 logcat是android中的一个命令行工具&#xff0c;可以用于得到程序的log信息。下面介绍 adb logcat中的详细参数命令以及如何才能高效的打印日志&#xff0c;或把日志保存到我们指定的位置。 可以输入 adb logcat --help&#xff0c;查看一下一些简…

VMware vSphere5.0关闭虚拟机电源时,报错从ESXI主机接收到错误

ESXI和VCENTER都是5.0版本的&#xff0c;有台虚拟机关机报错提示从ESXI主机接受到意外错误 具体报错信息如下&#xff1a; 从VCENTER平台对该虚拟机做任何操作都无法生效&#xff0c;后来查看了虚拟机的网络和端口&#xff0c;发现SSH能正常联通&#xff0c;进入虚拟机后使用命…

【云原生】docker 部署 Doris 数据库使用详解

目录 一、前言 二、数据分析概述 2.1 什么是数据分析 2.2 数据分析目的和意义 2.3 数据分析常用的技术和工具 2.3.1 编程语言 2.3.2 数据处理和分析库 2.3.3 数据可视化工具 2.3.4 数据库系统 2.3.5大数据处理框架 2.3.6 云服务和平台 2.3.7 其他工具 三、Doris介绍…

gdb中使用python脚本

1、入门案例 首先有1个a.cpp&#xff0c;代码如下&#xff1a; #include <map> #include <set> #include <iostream> #include <string>using namespace std;struct MyStruct {std::string mName;std::map<int, std::string> mField1;std::set…

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息&#xff1b; 2、DHT11采集病房温湿度信息&…

深度学习的发展历程

深度学习的起源 在机器学习中&#xff0c;我们经常使用两种方式来表示特征&#xff1a;局部表示&#xff08;Local Representation&#xff09;和分布式表示&#xff08;Distributed Representation&#xff09;。以颜色表示为例&#xff0c;见下图&#xff1a; 要学习到一种好…

iPhone手机清理软件:照片清理功能全解析

在数字化生活中&#xff0c;智能手机成为我们记录生活点滴的主要工具&#xff0c;尤其是iPhone&#xff0c;以其卓越的相机功能备受用户青睐。然而&#xff0c;成千上万的照片迅速堆积&#xff0c;不仅占用了大量存储空间&#xff0c;还使得设备运行缓慢。在众多解决方案中&…