Linux线程控制

目录

一、线程创建

1.1 pthread_create

1.2 线程传入启动函数参数方式

二、线程退出(pthread_exit函数 pthread_cancel函数)

三、线程等待

3.1 为什么要线程等待?

3.2 pthread_join函数

 四、线程分离

4.1 pthread_detach() 和 pthread_self()

五、pthread库维护线程的基本结构


一、线程创建

1.1 pthread_create

thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码 

1.2 线程传入启动函数参数方式

关于第四个参数,我们要注意其是void*类型指针,任意指针都可以传给线程!现在我们模拟一种错误的传参方式,传栈区资源,基于线程运行顺序产生的传参问题!

#define NUM 5
void* pthread_route(void* args)
{const char* name = (const char*)args;cout << "new thread create success, its name: "<<name<<endl;
}int main()
{pthread_t pid;for(int i=0;i<NUM;i++){char buffer[64];snprintf(buffer,sizeof(buffer),"thread %d",i+1);pthread_create(&pid,nullptr,pthread_route,buffer);}sleep(1);return 0;
}

运行结果: 

结果显示,我们的线程名字都是一样的!

首先我们可以知道这样的结果说明我们传过去的buffer地址是同一个地址! 有人可能疑惑不是每次循环后buffer的地址应该每次不一样吗? 这里结合函数栈帧知识,发现并没有函数调用,所以每次都还是在main函数栈帧内,所以每次的地址其实循环进来依旧是原来的地址!

有人又会提出,不对啊,按道理应该数字是根据i+1的来啊,按照我们route函数思路不应该全是5啊!

这就说明事实不是按照我们的所想,main函数创建线程后并不是立马执行线程对应的函数,当main函数创建完所有线程后,才执行线程的代码!而此时配合前面buffer首地址没变,而里面的数据再不断变换导致!

所以我们创建线程如果想看到线程私有的东西,不能传入共享的数据!我们不能传入栈内资源(这里没有多想其他场景),所以我们应该利用堆区资源!


正确的传参方式:数据放入堆区,传堆区指针!

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#define NUM 3
using namespace std;//每个线程独立的资源 线程ID + 线程buffer
class Thread_data
{
public:pthread_t _pid;char namebuffer[64];
};void *thread_route(void *args)
{Thread_data *p = static_cast<Thread_data *>(args);int cnt = 3;while (cnt--){cout << "new thread create success,its name: " << p->namebuffer << " &cnt: " << &cnt <<" cnt= "<<cnt<<endl;}delete p;return nullptr;
}int main()
{for (int i = 0; i < NUM; i++){Thread_data *pt = new Thread_data();snprintf(pt->namebuffer, sizeof(pt->namebuffer), "thread %d", i + 1);pthread_create(&pt->_pid, nullptr, thread_route, pt);}sleep(1);return 0;
}

运行结果: 


二、线程退出(pthread_exit函数 pthread_cancel函数)

void pthread_exit(void *value_ptr);
参数
value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

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


三、线程等待

3.1 为什么要线程等待?

为什么要有线程等待?回想以前的进程等待,父进程回收子进程,获取子进程退出信息!

对于已经退出的线程,其空间没有被释放,仍在进程的地址空间内!所以创造新的线程不会复用刚才退出线程的地址空间,这就造成了资源的浪费!

3.2 pthread_join函数

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

线程的调用逻辑函数返回值是void*,该函数会在线程结束前将线程结束状态码保存进pthread库中,打个比方它的数字状态码是10,然后通过强转(void*)10,将4字节整形放入8字节的指针中(Linux下指针默认是64位下!)。外部获取函数内部结果,我们需要传地址,所以要传void**!

需要知道的是,我们返回的值一定是右值!也就输说常变量、堆空间资源可以返回!栈上数据不能返回因为函数栈帧结束后栈空间数据会被销毁!

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>using namespace std;
#define NUM 5
class pthread_data
{
public:pthread_t tid;char buffer[128];
};
class Thread_ret
{
public:int exit_code;int exit_result;
};void* pthread_route(void* args)
{Thread_ret* ret = new Thread_ret();int cnt = 5;while(cnt){cout <<"cnt: " << cnt-- <<" &cnt:" <<&cnt<<endl;//BUG?sleep(1);}ret->exit_code = 106;ret->exit_result = 0;return (void*)ret;
}
int main()
{vector<pthread_data*> vp;for(int i=0;i<NUM;i++){pthread_data* pd = new pthread_data();pthread_create(&pd->tid,nullptr,pthread_route,pd);snprintf(pd->buffer,sizeof(pd->buffer),"thread :%d pid:0x%x ",i+1,pd->tid);vp.push_back(pd);}for(auto& e : vp){cout << "creat thread success: " << e->buffer<<endl;}for(auto&e :vp){Thread_ret* ret;pthread_join(e->tid,(void**)(&ret));cout <<"join :" <<e->buffer<<" success " <<"exit code:" <<ret->exit_code <<" exit result: " <<ret->exit_result<<endl;delete e;}cout <<"main quit!"<<endl;return 0;
}


 四、线程分离

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

4.1 pthread_detach() 和 pthread_self()

分离线程,可以是指定的其他线程,也可以是自己!

int pthread_detach(pthread_t thread);

pthread_self()获取线程自己的ID!


五、pthread库维护线程的基本结构

在前面我们了解到线程有属于自己的ID、私有栈等等结构,这些线程肯定需要被组织!如何组织呢?在Linux下线程库内用结构体(TCP)对每个线程进行组织!库其实本质是磁盘上的文件,在链接的时候将内容加载到内存的共享区内,也就是说我们的线程的属性存储在共享区内!线程的私有栈区实在共享区内!

 线程的局部存储:可以使一个全局变量让所有线程私有栈内创建一份!

在全局变量前面加上__thread 即可 !


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

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

相关文章

Gteam2.0免授权毛玻璃拟态UI带后台版本修复版

程序使用PHP7版本运行 后台信息/Admin 账号admin 密码123456 后台功能 多管理员、系统日志等等功能

机器学习---决策树的划分依据(熵、信息增益、信息增益率、基尼值和基尼指数)

1. 熵 物理学上&#xff0c;熵 Entropy 是“混乱”程度的量度。 系统越有序&#xff0c;熵值越低&#xff1b;系统越混乱或者分散&#xff0c;熵值越⾼。 1948年⾹农提出了信息熵&#xff08;Entropy&#xff09;的概念。 从信息的完整性上进⾏的描述&#xff1a;当系统的有序…

Ansible 自动化运维工具的使用

目录 一、Ansible简介 二、Ansible 的安装和使用 1.下载 2.使用 三、Ansible命令和模块 1.命令格式 2.命令行模块 &#xff08;1&#xff09;command 模块 &#xff08;2&#xff09;shell 模块 &#xff08;3&#xff09;cron 模块 &#xff08;4&#xff09;user 模…

Flink 如何处理反压?

分析&回答 什么是反压&#xff08;backpressure&#xff09; 反压通常是从某个节点传导至数据源并降低数据源&#xff08;比如 Kafka consumer&#xff09;的摄入速率。反压意味着数据管道中某个节点成为瓶颈&#xff0c;处理速率跟不上上游发送数据的速率&#xff0c;而…

【IIS搭建网站】本地电脑做服务器搭建web站点并公网访问「内网穿透」

1.前言 在网上各种教程和介绍中&#xff0c;搭建网页都会借助各种软件的帮助&#xff0c;比如网页运行的Apache和Nginx、数据库软件MySQL和MSSQL之类&#xff0c;为方便用户使用&#xff0c;还出现了XAMPP、PHPStudy、宝塔面板等等一系列集成服务&#xff0c;都是为了方便我们…

linux并发服务器 —— 多进程并发 - 进程间的通信及实践(五)

进程间的通信 进程是一个独立的资源分配单元&#xff0c;不能在一个进程中直接访问另一个进程的资源&#xff1b; 进程间通信&#xff08;IPC&#xff09;的目的&#xff1a; 1. 数据传输 - A进程发送数据给B进程 2. 通知事件 - eg. 进程终止通知父进程 3. 资源共享 - 多个…

go语言 go mod生成

1. go hello world 创建文件夹gotest&#xff0c;在其中创建test1.go文件&#xff0c;并写入 package mainimport ("fmt" )func main() {fmt.Println("hello world") } 运行命令 go run test1.go 可以看到输出hello world 2. cli 命令行的使用 代码如下…

机器人中的数值优化(六)—— 线搜索最速下降法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

重新理解百度智能云:写在大模型开放后的24小时

在这些回答背后共同折射出的一个现实是——大模型不再是一个单选题&#xff0c;而更是一个综合题。在这个新的时代帆船上&#xff0c;产品、服务、安全、开放等全部都需要成为必需品&#xff0c;甚至是从企业的落地层面来看&#xff0c;这些更是刚需品。 作者| 皮爷 出品|产…

c语言每日一练(13)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;上学期间将看学业情况更新。 五道选择题&#xff1a; 1、程序运行的结果…

Go死码消除

概念: 死码消除(dead code elimination, DCE) 是一种编译器优化技术, 作用是在编译阶段去掉对程序运行结果没有任何影响的代码 和 逃逸分析[1],内联优化[2]并称为 Go编译器执行的三个重要优化 效果: 对于 const.go代码如下: package mainimport "fmt"func max(a, b i…

一篇文章教会你如何编写一个简单的Shell脚本

文章目录 简单Shell脚本编写1. 简单脚本编写2. Shell脚本参数2.1 Shell脚本参数判断2.1.1 文件测试语句2.1.2 逻辑测试语句2.1.3 整数值测试语句2.1.4 字符串比较语句 3. Shell流程控制语句3.1 if 条件测试语句3.1.1 if...3.1.2 if...else...3.1.3 if...elif...else 4. Shell脚…

汽车自适应巡航系统控制策略研究

目 录 第一章 绪论 .............................................................................................................................. 1 1.1 研究背景及意义 ..........................................................................................…

文件夹中lib,dll含义

.dll文件是动态链接库&#xff08;Dynamic Link Library&#xff09;的缩写&#xff0c;它包含了一组可执行的函数和数据&#xff0c;供程序调用。它可以被多个应用程序共享和重用&#xff0c;减少了代码的冗余。通过动态链接库&#xff0c;可以实现代码的模块化和提高代码的复…

ELK安装、部署、调试(五)filebeat的安装与配置

1.介绍 logstash 也可以收集日志&#xff0c;但是数据量大时太消耗系统新能。而filebeat是轻量级的&#xff0c;占用系统资源极少。 Filebeat 由两个主要组件组成&#xff1a;harvester 和 prospector。 采集器 harvester 的主要职责是读取单个文件的内容。读取每个文件&…

机器学习技术(六)——有监督学习算法之线性回归算法实操

机器学习技术&#xff08;五&#xff09;——有监督学习之线性回归算法实操 引言&#xff1a; 机器学习监督算法是一种基于已有标记数据的学习方法&#xff0c;通过对已知输入和输出数据的学习&#xff0c;建立一个模型来预测新的输入数据的输出。这种算法模仿人类的学习过程&a…

安防监控/视频汇聚平台EasyCVR调用rtsp地址返回的IP不正确是什么原因?

安防监控/云存储/磁盘阵列存储/视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RT…

docker 笔记2 Docker镜像和数据卷

参考&#xff1a; 1.镜像是什么&#xff1f;&#xff08;面试题&#xff09; 是一种轻量级、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文…

算法笔记——路径问题

在引入介绍如何写一个算法的时候&#xff0c;我们先引入一个题作为例子 1137. 第 N 个泰波那契数 - 力扣&#xff08;LeetCode&#xff09; 作为刚开始学习算法的我们&#xff0c;看到这个题目的时候&#xff0c;应该想好以下的问题&#xff1a; 1.状态表示 我们要用什么来表…

Windows7安装SSH客户端的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…