Linux学习——线程的控制
目录
编辑
一,线程的创建
二,线程的退出
1,在子线程内return
2,使用pthread_exit(void*)
三,线程等待
四,线程获取自己的id值
五,线程取消
六,线程分离
一,线程的创建
在对进程控制之前,首先要做的便是创建一个线程。创建方法如下:
使用的创建方法叫做pthread_create。
参数介绍:
thread
:线程idattr
:线程属性,直接设为nullstart_routine
:函数指针arg
:这个参数会传递进start_routine
的void*
参数中。
例子:
#include<iostream>#include<pthread.h>#include<unistd.h>using namespace std;void* hander(void* args)//新线程执行的方法{while(true){sleep(1);cout << "i am new thread" << endl;}}int main(){pthread_t td;pthread_create(&td, nullptr, hander, nullptr);//创建好新线程以后,新线程会去执行传入的hander方法。while(true)//主线程会继续向下执行自己的方法{sleep(1);cout << "i am main thread" << endl;}return 0;}
执行这个代码以后结果如下:
在这里要注意,在使用g++编译时要加上-lpthread。因为线程库是一个第三方库,但是是安装在系统中的所以只需要-l便可以连接到pthread库。
二,线程的退出
1,在子线程内return
线程的退出有多种方式,先来看看最基本的一种退出方式,代码如下:
void *hander(void *args)
{string name = static_cast<const char *>(args);int cnt = 5;while (cnt--){cout << "i am new thread" << name << endl;sleep(1);}return nullptr;//最基本的退出线程的方式便是直接在子线程内部使用return的方式退出
}class data
{
public:char buf[64];int i;
};int main()
{for (int i = 1; i <= NUM; i++) // 创建一批线程{data *m = new data();snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);pthread_t td;pthread_create(&td, nullptr, hander, (void *)m->buf);}while (true){cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;sleep(1);}return 0;
}
在使用这种方式退出时,主线程在子线程退出以后还会继续执行。但是如果是子线程不退出而主线程先退出呢?像这样:
void *hander(void *args)
{string name = static_cast<const char *>(args);int cnt = 5;while (true)//子线程一直在死循环{cout << "i am new thread" << name << endl;sleep(1);}return nullptr;
}class data
{
public:char buf[64];int i;
};int main()
{for (int i = 1; i <= NUM; i++) // 创建一批线程{data *m = new data();snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);pthread_t td;pthread_create(&td, nullptr, hander, (void *)m->buf);}int cnt = 5;while (cnt--)//主线程在cnt减到零时就退出{cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;sleep(1);}return 0;
}
这样的话只要主线程退出了,这个进程都会直接结束。 如下:
2,使用pthread_exit(void*)
这个函数是线程库提供给我们的专门用于线程退出的函数,他的参数可以直接设置为nullptr。使用方式如下:
void *hander(void *args)
{string name = static_cast<const char *>(args);int cnt = 5;while (cnt--){cout << "i am new thread" << name << endl;sleep(1);}pthread_exit(nullptr);//使用pthread_exit()退出线程。
}class data
{
public:char buf[64];int i;
};int main()
{for (int i = 1; i <= NUM; i++) // 创建一批线程{data *m = new data();snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);pthread_t td;pthread_create(&td, nullptr, hander, (void *)m->buf);}while (true){cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;sleep(1);}return 0;
}
使用pthread_exit退出的效果和在子线程内使用return退出的效果一样。
##注意## 线程的退出不能使用exit,因为exit的本质其实是向进程发信号,所以exit是专门用于进程退出的。同样的,线程的退出也不需要返回errno,因为如果一个线程因为异常退出的话整个进程都会退出,进程返回errno就可以了。
三,线程等待
和进程一样,线程也需要等待。等待的目的如下:
1,回收新线程对应的内核资源。
2,接收新线程返回的数据。
线程等待函数: int pthread_join(pthread_t thread, void **retval)
thread:表示要等待线程的pid
reval:接收数据并将数据带出。
使用如下:
class thread
{
public:int _num; // 线程的编号char _buf[64]; // 线程的名字pthread_t _tid; // 线程的id
};void *start_routine(void *args)
{int cnt = 5;while (cnt--){sleep(1);thread *_td = static_cast<thread *>(args);cout << "i am new thread:" << _td->_buf << ":" << _td->_num<< ":" << _td->_tid << endl;}pthread_exit(nullptr);//线程退出
}int main()
{vector<thread*> threads;for (int i = 1; i <= 10; i++)//创建线程{thread *td = new thread;td->_num = i;snprintf(td->_buf, sizeof(td->_buf), "%s-%d", "thread:", i);pthread_create(&td->_tid, nullptr, start_routine, (void *)td);threads.push_back(td);}for(auto e:threads){void *ret = nullptr;pthread_join(e->_tid, &ret);//回收线程cout << "等待成功"<< " tid:" << e->_tid << endl;}cout << "等待结束" << endl;return 0;
}
以上的代码便演示了如何用pthread_join进行线程的等待效果如下:
那该函数里面的里面的返回值有什么作用呢?其实这个返回值就是用来带出退出码的。过程如下:
添加打印退出码的信息以后结果如下:
那为什么reval的类型是二级指针类型呢?这其实是因为线程结束后,退出信息会写入到线程库内部。线程库内部的退出码便是void*类型的。此时我们要想的便是获取这个退出码了,如何获取呢?因为pthread_join()的返回值是int类型的,所以我们便不能直接让pthread_join()直接返回一个void*类型的变量,所以只能自己在用户层定义一个void*类型的retval然后retval的地址传入进去获取返回值了。
四,线程获取自己的id值
使用 pthread_t pthread_self(void)可以获取到当前线程的id值。
示例代码:
#include <iostream>
#include <pthread.h>
#include <vector>
#include <unistd.h>
using namespace std;void *Done(void *args)
{uint64_t i = (uint64_t)args;string name = "thread_" + to_string(i);sleep(1);cout << name << "id :" << pthread_self() << endl;//使用pthread_self()打印线程id值。}int main()
{vector<pthread_t> wait;for (uint64_t i = 1; i <= 4; i++){pthread_t td;pthread_create(&td, nullptr, Done, (void *)i); // 创建线程wait.push_back(td);sleep(2);}for (auto e : wait) // 等待线程{pthread_join(e, nullptr);}return 0;
}
如果用16进制打印便是下面这样的:
![]()
其实线程的id就是一些地址。
五,线程取消
进行线程取消的函数叫做pthread_cancel(pthread_t thread)。线程取消的前提是线程先运行起来,然后才能取消。
实验代码:创建线程,然后取消一半线程,观察现象。
class thread
{
public:int _num; // 线程的编号char _buf[64]; // 线程的名字pthread_t _tid; // 线程的id
};void *start_routine(void *args)
{int cnt = 5;while (cnt--){sleep(1);thread *_td = static_cast<thread *>(args);cout << "i am new thread:" << _td->_buf << ":" << _td->_num<< ":" << _td->_tid << endl;}pthread_exit((void*)100);
}int main()
{vector<thread *> threads;for (int i = 1; i <= 10; i++){thread *td = new thread;td->_num = i;snprintf(td->_buf, sizeof(td->_buf), "%s-%d", "thread:", i);pthread_create(&td->_tid, nullptr, start_routine, (void *)td->_buf);threads.push_back(td);}for (int i = 0;i<threads.size()/2;i++)//取消一半的线程{pthread_cancel(threads[i]->_tid);}for (auto e : threads)//等待{void *ret = nullptr;pthread_join(e->_tid, &ret);cout << "等待成功"<< " tid:" << e->_tid << "quit code: " << (long long)(ret) << endl;delete e;}cout << "等待结束" << endl;return 0;
}
运行结果如下:
可以看到如果取消线程,那线程还是会被等待然后退出,退出码是-1。其实这是一个宏:
六,线程分离
线程分离使用到的函数 int pthread_detach(pthread_t thread)。先来说明一下,新创建的线程默认是joinable的。但是如果我的主线程并不关心当前的线程的返回值,那当前的线程便与我无关。那我的主线程去等待当前的线程便对我的主线程是一种负担。这个时候便可以来进行线程分离。线程的分离方式有两种:1,主线程去分离子线程 2,子线程自己进行分离。
示例代码:
1,主线程进行分离
#include<iostream>
#include<pthread.h>
#include<vector>
#include<unistd.h>
using namespace std;void* Done(void* args)
{uint64_t i = (uint64_t)args;string name = "thread_" + to_string(i);int cnt = 5;while (cnt--){sleep(1);cout << name << "running....." << endl;sleep(3);}
}int main()
{vector<pthread_t> wait;for (uint64_t i = 1; i <= 4; i++){pthread_t td;pthread_create(&td, nullptr, Done, (void*)i);//创建线程wait.push_back(td);sleep(3);//先休眠三秒,再进行线程分离pthread_detach(td);//主线程子集分离}for(auto e:wait)//等待线程{int n = pthread_join(e,nullptr);cout << n << " " << endl;//打印等待的返回值,0表示成功,其它表示失败。}return 0;
}
2,子线程自己主动分离
#include <iostream>
#include <pthread.h>
#include <vector>
#include <unistd.h>
using namespace std;void *Done(void *args)
{uint64_t i = (uint64_t)args;string name = "thread_" + to_string(i);pthread_detach(pthread_self()); // 子线程自己自动分离int cnt = 5;while (cnt--){cout << name << "running....." << endl;sleep(1);}
}int main()
{vector<pthread_t> wait;for (uint64_t i = 1; i <= 4; i++){pthread_t td;pthread_create(&td, nullptr, Done, (void *)i); // 创建线程wait.push_back(td);}for (auto e : wait) // 等待线程{int n = pthread_join(e, nullptr);cout << n << " " << endl; // 打印等待的返回值,0表示成功,其它表示失败。}return 0;
}
相关文章:

Linux学习——线程的控制
目录 编辑 一,线程的创建 二,线程的退出 1,在子线程内return 2,使用pthread_exit(void*) 三,线程等待 四,线程获取自己的id值 五,线程取消 六,线程分离 一,线程的创建 在对…...
Rust常用特型之Drop特型
Rust常用特型之Drop特型.md在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。 今天,我们主要学习Drop特型。 (注:本文更多的是对《Programing Rust 2nd Edition》的自己翻译和理解&…...

嵌入式 Linux 学习
在学习嵌入式 Linux 之前,我们先来了解一下嵌入式 Linux 有哪些东西。 1. 嵌入式 Linux 的组成 嵌入式 Linux 系统,就相当于一套完整的 PC 软件系统。 无论你是 Linux 电脑还是 windows 电脑,它们在软件方面的组成都是类似的。 我们一开电…...
Makedown语法
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...
SQLite语句
1.重写SQLiteOpenHelper // 例. public class MySQLiteOpenHelper extends SQLiteOpenHelper {public MySQLiteOpenHelper(Nullable Context context, Nullable String name, Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, ve…...

Spring揭秘:Aware接口应用场景及实现原理!
内容概要 Aware接口赋予了Bean更多自感知的能力,通过实现不同的Aware接口,Bean可以轻松地获取到Spring容器中的其他资源引用,像ApplicationContext、BeanFactory等。 这样不仅增强了Bean的功能,还提高了代码的可维护性和扩展性&…...

校园小情书微信小程序,社区小程序前后端开源,校园表白墙交友小程序
功能 表白墙卖舍友步数旅行步数排行榜情侣脸漫画脸个人主页私信站内消息今日话题评论点赞收藏 效果图...
从Pandas到Polars :数据的ETL和查询
对于我们日常的数据清理、预处理和分析方面的大多数任务,Pandas已经绰绰有余。但是当数据量变得非常大时,它的性能开始下降。 本文将介绍如何将日常的数据ETL和查询过滤的Pandas转换成polars。 图片 Polars的优势 Polars是一个用于Rust和Python的Data…...

Node.Js编码注意事项
Node.js 中不能使用 BOM 和 DOM 的 API,可以使用 console 和定时器 APINode.js 中的顶级对象为 global,也可以用 globalThis 访问顶级对象 浏览器端js的组成 Node.js中的JavaScript组成 相比较之下发现只有console与定时器是两个API所共有的ÿ…...

floodfill算法题目
前言 大家好,我是jiantaoyab,在下面的题目中慢慢体会floodFill算法,虽然是新的算法,但是用的思想和前面的文章几乎一样,代码格式也几乎一样,但不要去背代码 图像渲染 https://leetcode.cn/problems/flood…...

AI相关的实用工具分享
AI实用工具大赏:赋能科研与生活,探索AI的无限可能 前言 在数字化浪潮汹涌而至的今天,人工智能(AI)已经渗透到我们生活的方方面面,无论是工作还是生活,都在悄然发生改变。AI的崛起不仅为我们带…...

K8s — PVC|PV Terminating State
在本文中,我们将讨论PV和PVC一直Terminating的状态。 何时会Terminting? 在以下情况下,资源将处于Terminating状态。 在删除Bounded 状态的PVC之前,删除了对应的PV,PV在删除后是Terminting状态。删除PVC时,仍有引用…...

C语言 --- 指针(5)
目录 一.sizeof和strlen对比 1.sizeof 2.strlen 3.strlen 和sizeof的对比 二.数组和指针笔试题目详解 回顾:数组名的理解 1.一维数组 2.字符数组 代码1: 代码2: 代码3: 代码4: 代码5: 代码6&am…...

Android Studio Iguana | 2023.2.1版本
Android Gradle 插件和 Android Studio 兼容性 Android Studio 构建系统基于 Gradle,并且 Android Gradle 插件 (AGP) 添加了一些特定于构建 Android 应用程序的功能。下表列出了每个版本的 Android Studio 所需的 AGP 版本。 如果特定版本的 Android Studio 不支持…...

并查集(蓝桥杯 C++ 题目 代码 注解)
目录 介绍: 模板: 题目一(合根植物): 代码: 题目二(蓝桥幼儿园): 代码: 题目三(小猪存钱罐): 代码: …...

MapReduce内存参数自动推断
MapReduce内存参数自动推断。在Hadoop 2.0中,为MapReduce作业设置内存参数非常繁琐,涉及到两个参数:mapreduce.{map,reduce}.memory.mb和mapreduce.{map,reduce}.java.opts,一旦设置不合理,则会使得内存资源浪费严重&a…...
pyside6 pytq PyDracula QVideoWidget视频只有画面没有声音
解决方案: 先不使用框架,纯pyside6代码,如果添加视频有画面有声音,那可以排除是硬件问题,如果没有画面只有声音,可能是视频解码器无法解码,换个格式的视频文件如果只有使用PyDracula 出问题&am…...

Axure基础 各元件的作用及介绍
图像热区 增加按钮或者文本的点击区域,他是透明的,在预览时看不见。 动态面板 用来绘制一下带交互效果的元件,他是动态的,如轮播图,一个动态面板里可以有多个子面板,每一个子面板对应着不同的效果。 他…...

学习Java的第六天
目录 一、变量 1、变量的定义 2、变量的声明格式 3、变量的注意事项 4、变量的作用域 二、常量 三、命名规范 Java 语言支持如下运算符: 1、算术运算符 解析图: 示例: 2、赋值运算符 解析图: 示例: 3、关…...

基于Spring Boot+ Vue的房屋租赁系统
末尾获取源码作者介绍:大家好,我是墨韵,本人4年开发经验,专注定制项目开发 更多项目:CSDN主页YAML墨韵 学如逆水行舟,不进则退。学习如赶路,不能慢一步。 目录 一、项目简介 二、开发技术与环…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...