【Linux系统化学习】线程控制

目录

前言

POSIX线程库

线程控制

创建线程

线程终止

pthread_exit()函数

pthread_cancel()函数(会在下面线程等待部分详解)

线程等待 

pthread_join()函数

获取线程退出码

分离线程

线程取消(pthread_cancel()函数)

线程ID及进程地址空间分布


前言

好久不见!各位支持我的IT人,前段时间由于准备蓝桥杯需要学习一段时间算法就停止更新Linux的内容;由于自己也是算法初学者,想着蓝桥杯突击一个多月算法就行了没必要更新算法博客,后面还会专门学习算法到那时候在更新算法的内容。停更前对Linux中的线程做了一个简单的介绍,在接下来的几篇文章我们会深入探讨Linux的线程这方面的问题。线程这方面学习完后,Linux系统编程就结束了,接下来便是网络编程和数据库;在网络编程学习中会编写我的第一个项目,到时候希望大家多多支持!!!


POSIX线程库

线程是操作系统中的一个概念,在Linux这个特定的操作系统中是没有线程这个概念的;是用轻量级进程(LWP)来实现所谓操作系统中的线程的;因此Linux操作系统只会提供轻量级进程的的系统调用,不会提供线程创建的接口。基于此原因我们要在操作系统和用户之间包含一个软件层对用户层向上提供线程的控制接口,对操作系统提供轻量级进程的控制接口。这并不繁琐,可以让操作系统和用户层实现解耦,反而这是Linux操作系统的一大亮点。这个软件层就是POSIX线程库,这是一个原生库,对于每个Linux系统都会包含这个库;因此我们在用户层创建线程时需要引入这个库。


线程控制

创建线程

函数原型

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);

功能:创建一个新的线程

参数:

  • thread:返回线程ID(这是一个输出行参数)
  • attr:设置线程的属性,attr为NULL表示使用默认属性
  • start_routine:是个函数地址,线程启动后要执行的函数
  • arg:传给线程启动函数的参数

注意:第三个参数是一个返回值为void*参数为void*的函数指针,第四个void*参数作为第三个函数指针的参数。 

返回值:成功返回0;失败返回错误码 

简单代码示例

void* ThereadTountine(void* args)
{const char * p = (char *) args;//这里也可以使用c++11中的安全类型转换while(true){sleep(1);cout<<p<<endl;}
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThereadTountine,(void*)"new thread-1");while(true){sleep(1);cout<<"main thread"<<endl;}return 0;
}

  

 这里只是创建了一个新线程,我们可以使用循环配合这个线程创建函数创建更多的线程。

注意:这里的第四个参数不仅仅可以像上面一样传入一个常量字符串,甚至是可以传入一个自定义对象。

线程终止

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

  • 1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  • 2. 线程可以调用pthread_ exit终止自己。(新线程调用的函数不可以使用exit函数,因为exit使用来终止整个进程的,而线程只是整个进程中的一个执行流)
  • 3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

pthread_exit()函数

功能:终止线程

函数原型:

void pthread_exit(void *value_ptr);

参数:

value_ptr:value_ptr不要指向一个局部变量。 

返回值:

无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。 

pthread_cancel()函数(会在下面线程等待部分详解)

功能:

取消一个执行中的线程

函数原型:

int pthread_cancel(pthread_t thread);

参数:

  • thread:线程ID
  • 返回值:成功返回0;失败返回错误码

线程等待 

我们在进程学习的时候,当一个进程退出后没有被等待会产生僵尸进程,造成资源浪费。那么对于线程来说,当一个线程退出的时候也会产生类似僵尸进程的问题。因此我们要等待线程,不仅是为了方式僵尸问题,而且可以获取线程的返回值。

pthread_join()函数

功能:等待线程结束

函数原型

int pthread_join(pthread_t thread, void **value_ptr);

参数:

  • thread:线程ID
  • value_ptr:它指向一个指针,后者指向线程的返回值

返回值:成功返回0;失败返回错误码 

获取线程退出码

线程创建函数的返回值为void * ,而线程等待的第二个参数为void**;这个参数是一个输出型参数,可以获取到函数退出的推出信息。

void* ThereadTountine(void* args)
{const char * p = (char *) args;//这里也可以使用c++11中的安全类型转换int cnt=5;while(cnt--){sleep(1);cout<<p<<endl;}return (void*)"new thread-1 done";
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThereadTountine,(void*)"I am new thread-1");void * ret=nullptr;int n = pthread_join(tid,&ret);cout<<(const char *) ret<<endl;return 0;
}

对于线程出异常我们根本不许用考虑,因为当某个线程出异常的时候,整个进程都会挂掉;等待线程已经没有什么意义了。

分离线程

如果我们线程一直不退出时,主线程会一直阻塞等待这个线程。我们可以将这个线程设置为游离状态。

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

因此我们可以根据需求动态决策,线程的状态。 

函数原型

如果想要分离一个线程,可以在主线程中调用函数将新线程分离,也可以在新线程中将自已与主线程分离。

int pthread_detach(pthread_t thread);//分离自己
pthread_detach(pthread_self());

注意:joinable和分离是冲突的,一个线程不可以既是joinable又是分离的。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void *ThereadTountine(void *args)
{// 新线程和主线程分离//pthread_detach(pthread_self());const char *p = (char *)args; // 这里也可以使用c++11中的安全类型转换int cnt = 5;while (cnt--){sleep(1);cout << p << endl;}return (void *)"new thread-1 done";
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThereadTountine, (void *)"I am new thread-1,running  ~ ~ ");//主线程和新线程分离pthread_detach(tid);sleep(1);//保证新线程已经执行了一秒int n = pthread_join(tid, nullptr);cout << n << endl;return 0;
}

 

线程取消(pthread_cancel()函数)

void *ThereadTountine(void *args)
{const char *p = (char *)args; // 这里也可以使用c++11中的安全类型转换int cnt = 5;while (cnt--){sleep(1);cout << p << endl;}
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThereadTountine, (void *)"I am new thread-1,running  ~ ~ ");sleep(5);//保证新线程已经执行了一秒//取消新线程int n = pthread_cancel(tid);cout<<"cancle n:"<<n<<endl;void* ret=nullptr;n = pthread_join(tid, &ret);cout<<"join n:" << n << "    thread return:"<<(int64_t) ret<<endl;return 0;
}

 

当线程被取消后其退出码为-1;当线程被分离时可以被取消,但是不可以被等待。因此对于分离的线程在运行时我们可以对其取消操作


线程ID及进程地址空间分布

在新线程中可以使用pthread_self(),来获取自身ID

 这个ID看起来非常的大,我们可以尝试转化为16进制进行打印; 

转化为十六进制后,这个线程ID和虚拟地址非常的类似。

void* ThereadTountine(void* args)
{const char * p = (char *) args;//这里也可以使用c++11中的安全类型转换while(true){sleep(1);cout<<p<<endl;printf("new thread ID : %x\n",pthread_self());//获取自身线程ID}
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThereadTountine,(void*)"I am new thread-1");while(true){sleep(1);cout<<"I am main thread"<<endl;printf("main ID : %x\n",pthread_self());}return 0;
}

当我们在用户层创建了n个线程时,通过线程库操作系统会创建n个轻量级进程,对于这些轻量级进程来说可以复用进程那一套管理方案来解决。我们所谓的“线程”是不过是用户级线程,对于这些线程来说我们也要进行管理——“先描述,在组织”,因此我们要有一个TCB(Thread Control Block,线程控制块),这个TCB并不存在于操作系统中,存在于库中。TCB中含有LWP ID 等关于这个线程的各种属性。但是对于每个单独的线程来说,还要包含存储独立上下文的存储结构和独立的栈空间。库是共享的在共享区中只包含一次就可以,然而线程可以是很多的,我们可以将每个线程的TCB和进程上下文的存储空间和独立的栈描述起来,然后使用一个数组将其组织起来;将每个线程的起始地址作为TID向上返回给用户。

两个小问题

线程可以fork()创建子进程吗?

可以,在前面进程的学习中我们是只含有一个执行流的进程,在这个进程中创建子进程;现在我们这个进程中的进程中含有多个执行流,只不过在这众多执行流中fork子进程。

线程可以进行程序替换吗?

可以,但不推荐!因为线程公用主线程的公共资源,进行程序替换可能会导致,整个进程被替换;应该在众多线程中的一个线程中fork一个进程后进行程序替换。 


今天对Linux下线程控制的各种操作的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!

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

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

相关文章

open Gauss 数据库-06 openGauss数据库安全指导手册5.0.0

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss数据库安全指导 1 用户权限控制 1.1 实验介绍 1.1.1 关于本实验 1.1.2 实验目的 1.2 用户…

富文本编辑器(wangEdit)+(jquery.wordexport)实现web版在线编辑导出

小插曲&#xff1a;最开始的方向是Html5的contenteditable"true"的文档可编辑属性。只能修改文档文字内容&#xff0c;不能修改样式&#xff0c;如修改字体&#xff0c;字号&#xff0c;颜色等。于是用了第一款&#xff08;quil&#xff09;富文本插件。只能说一般&a…

IEEE PDF eXpress Validating Pdf..之后Error in converting file

在将自己写好的pdf论文转化为IEEE出版的pdf论文格式的时候&#xff0c;错误如下图&#xff1a; 解决办法如下&#xff1a;失败之后&#xff0c;那里有一个选项按钮&#xff0c;叫做manual request&#xff0c;也就是人工转换&#xff0c;点那个申请就可以了&#xff0c;然后也挺…

jdk keytool.exe生成keystore https证书,利用jks2pfx转换nginx证书,nginx配置证书

jdk keytool.exe生成keystore https证书,利用jks2pfx转换nginx证书,nginx配置证书 由于项目需要https访问,使用ngigx配置https证书,在用nginx代理到 各个tomcat,比tomcat直接配置https证书方便,记录下自己制作nginx https证书文件,以及配置的过程. 由于证书不是证书机构颁…

我与C++的爱恋:类和对象(四)

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 朋友们大家好&#xff01;本篇是类和对象的最后一个部分。 一、static成员 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之…

Ctrl + B 复制图片Base64

简介 这是一个专为Windows系统设计的工具&#xff0c;用于快速获取资源管理器&#xff08;文件夹&#xff09;中选中图片文件的Base64编码。 背景 由于工作需求经常需要获取图片的Base64并粘贴到postman中调用接口。最开始的做法是使用在线工具将图片转换为Base64编码&#…

DBUnit增强:填充随机数据和相对时间数据

痛点 测试环境验证时&#xff0c;遇到与当前相对时间相关的测试吗&#xff1f;准备一份SQL&#xff1f;隔一段时间就不能用了。每过一段时间去更新脚本或重置系统时间&#xff1f;看上去也不是很合适的解决方案。依赖数据测试时要重新做&#xff0c;演示时候得全部改&#xff…

Mini-Gemini: 探索多模态视觉语言模型的新境界

一、背景 在数字化时代&#xff0c;人工智能的发展正以前所未有的速度推进。特别是在多模态学习领域&#xff0c;结合视觉和语言的能力已成为研究的热点。最近&#xff0c;一篇名为“Mini-Gemini: Mining the Potential of Multi-modality Vision Language Models”的文章在arX…

05 MySQL--字段约束、事务、视图

1. CONSTRAINT 约束 创建表时&#xff0c;可以给表的字段添加约束&#xff0c;可以保证数据的完整性、有效性。比如大家上网注册用户时常见的&#xff1a;用户名不能为空。对不起&#xff0c;用户名已存在。等提示信息。 约束包括&#xff1a; 非空约束&#xff1a;not null检…

【C++类和对象】const成员函数及流插入提取

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

Elasticsearch 开放 inference API 增加了对 OpenAI chat completions 的支持

作者&#xff1a;Tim Grein 我们很高兴地宣布在 Elasticsearch 中推出的最新创新&#xff1a;在 Elastic 的 inference API 中集成了 OpenAI Chat Completions 功能。这一新特性标志着我们在整合尖端人工智能能力至 Elasticsearch 的旅程中又迈出了一步&#xff0c;提供了生成类…

凭证自动拆分

凭证自动拆分 一、设置拆分中间科目 OBX2配置凭证拆分的中间科目 二、销售开票凭证拆分 SE18创建BADI "FI_BILL_ISSUE_SPLIT"的实施 修改实施类&#xff1a;ZCL_FI_BILL_ISSUE_SPLIT中IF_EX_FI_BILL_ISSUE_SPLIT~ACTIVATE_AUTOMATIC_SPLIT方法&#xff0c;代码…

ruoyi-vue前端的一些自定义插件介绍

文章目录 自定义列表$tab对象打开页签关闭页签刷新页签 $modal对象提供成功、警告和错误等反馈信息&#xff08;无需点击确认&#xff09;提供成功、警告和错误等提示信息&#xff08;类似于alert&#xff0c;需要点确认&#xff09;提供成功、警告和错误等提示信息&#xff08…

vue 的生命周期--图解

生命周期函数中的this指向是vm 或 组件实例对象。 常用的生命周期钩子&#xff1a; mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。 关于销毁Vue实例 销毁后借助Vu…

常见排序算法(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,计数排序,基数排序,桶排序)

一.排序的概念 1.排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作 2.稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排…

MATLAB设置变量

您可以通过简单的方式分配变量。例如&#xff0c; 示例 x 3 %定义x并用值初始化它 MATLAB将执行上述语句并返回以下结果- x 3 它创建一个名为x的1乘1矩阵&#xff0c;并将值3存储在其元素中。再举一个实例&#xff0c; 示例 x sqrt(16) %定义x并用表达式初始化它 MATLAB将…

自动化测试Selenium(3)

目录 WebDriver相关API 打印信息 打印title 打印url 浏览器的操作 浏览器最大化 设置浏览器的宽,高 操作浏览器的前进, 后退, 刷新 控制浏览器滚动条 键盘事件 键盘单键用法 键盘组合按键用法 鼠标事件 WebDriver相关API 打印信息 打印title 即打印该网址的标题.…

在PostgreSQL中,如何创建一个触发器并在特定事件发生时执行自定义操作?

文章目录 解决方案示例代码1. 创建自定义函数2. 创建触发器 解释 在PostgreSQL中&#xff0c;触发器&#xff08;trigger&#xff09;是一种数据库对象&#xff0c;它能在特定的事件&#xff08;如INSERT、UPDATE或DELETE&#xff09;发生时自动执行一系列的操作。这些操作可以…

css-Echarts图表初始显示异常非完全显示

1.echarts图表初始加载异常 2.问题原因 初次加载时&#xff0c;由于外层使用%比 echarts dom元素没有完全加载完成&#xff0c;canvas绘画继承本身宽高&#xff0c;造成Echarts图表初始显示异常非完全显示。 3.使用echarts图表可参考以下代码&#xff08;实现一定的自适应&am…

MyBatis 核心配置讲解(上)

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的互金摸鱼侠。 前两篇的文章中我们分别介绍了 MyBatis 和 MyBaits 的应用组成&#xff0c;到这里基础篇的内容就结束了。 从今天开始&#xff0c;我们正式进入 MyBatis 学习的第二阶段&#xff1a;MyBatis 的…