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

线程控制(创建、终止、等待、分离)

目录

1.前言

2.创建线程 

pthread_create函数

3.线程终止

pthread_exit函数

pthread_cancel函数

4.线程等待

5.线程分离


1.前言

在Linux系统中,并不存在真正的线程,只有轻量级进程。所以,Linux系统只提供了操作轻量级进程的系统调用接口,并不提供直接操作线程的系统调用接口。但是,对于用户来说,用户想要对线程进行操作,只认线程相关的接口。于是,有人对轻量级进程的系统调用接口进行封装,转换成线程相关的接口语义给用户使用。

封装其实就是封装成库,这个库被叫做Linux的原生线程库:

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头。
  • 要使用这些函数库,要通过引入头文<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项 

2.创建线程 

pthread_create函数

pthread库中创建线程的函数为pthread_create,创建出的新线程和主线程谁先运行时不确定的,由调度器说了算。

功能:用于创建一个新的线程。

函数原型:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                                                            void *(*start_routine) (void*), void *arg);

参数:

  • pthread_t *thread:这是一个输出型参数,用于存储新线程的标识符(线程ID)。线程创建成功后,系统会将线程ID写入该指针指向的内存。原生线程库中还提供了一个让线程获取自己的线程id的方法:pthread_t pthread_self(void)

  • const pthread_attr_t *attr:指向线程属性的指针,用于设置线程的属性(如栈大小、调度策略等),如果为 NULL,则使用默认属性

  • void *(*start_routine) (void *):

    • 线程启动后执行的函数指针。该函数必须返回 void * 并接受一个 void * 类型的参数。

    • 线程从该函数的起始处开始执行,函数返回时线程终止。

  • void *arg:传递给 start_routine 函数的参数。如果需要传递多个参数,可以将它们封装在一个结构体中,然后传递结构体的指针

返回值:

  • 成功:返回 0

  • 失败:返回错误码(非零值),常见的错误码包括:

    • EAGAIN:系统资源不足,无法创建线程。

    • EINVAL:线程属性无效。

    • EPERM:没有权限设置调度策略或参数。

使用示例:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* handler(void* arg)
{int cnt = 5;while(cnt--){cout << "I am a thread" << endl;sleep(1);}return nullptr;
}int main()
{pthread_t thread_id = 0;int ret = pthread_create(&thread_id, NULL, handler, NULL);if(ret != 0){cout << "create error" << endl;}sleep(6);return 0;
}

运行结果:

当我们的代码创建出一个线程之后,新线程就会和主线程并发执行自己的代码。如果主线程先退,表示进程退出,新线程也会结束,如果新线程先结束,主线程不会直接结束,会等自己的代码运行完之后再结束。

3.线程终止

终止一个线程的方法有三种:

  • return:使用return语句退出。
  • pthread_exit:线程可以调用pthread_exit函数终止自己。
  • pthread_cancel:一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

pthread_exit函数

功能:终止当前进程的执行。

函数原型:void pthread_exit(void *retval)

参数:

  • void *retval:

    • 线程的返回值,通常是一个指向某个数据的指针。

    • 如果不需要返回值,可以传递 NULL

    • 该返回值可以被其他线程通过 pthread_join 获取

返回值:该函数没有返回值。

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

使用示例:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* handler(void* arg)
{int cnt = 3;while(cnt--){cout << "I am a thread" << endl;sleep(1);if(cnt == 1){cout << "called pthead_exit" << endl;pthread_exit(NULL);}}return nullptr;
}int main()
{pthread_t thread_id = 0;int ret = pthread_create(&thread_id, NULL, handler, NULL);if(ret != 0){cout << "create error" << endl;}sleep(5);return 0;
}

运行结果:

pthread_cancel函数

功能:用于请求取消(终止)指定的线程。

函数原型:int pthread_cancel(pthread_t thread)

参数:

  • pthread_t thread:目标线程的标识符(线程ID),即需要取消的线程。

返回值:

  • 成功:返回 0

  • 失败:返回错误码(非零值)。

使用示例:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* handler(void* arg)
{int cnt = 10;while(cnt--){cout << "new thread say: I am runnig" << endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid = 0;int ret = pthread_create(&tid, NULL, handler, NULL);if(ret != 0){cout << "create thread error" << endl;}cout << "create thread success" << endl;sleep(5);ret = pthread_cancel(tid);if(ret != 0){cout << "cancel thread error" << endl;}cout << "cancel thread success" << endl;return 0;
}

运行结果:

4.线程等待

在进程控制中,如果一个子进程退出,父进程需要对子进程进行回收,也就是需要进行进程等待,不然就会造成僵尸进程而引发资源泄漏问题;在线程这里,一个线程退出后,主线程需要对其进行回收,不然也会产生类似的问题。

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

pthread_join函数

功能:用于等待指定的线程终止,并获取该线程的返回值。

函数原型:int pthread_join(pthread_t thread, void **value_ptr)

参数:

  • thread: 要等待的线程的标识符(pthread_t 类型)。

  • value_ptr: 指向一个指针的指针,用于存储目标线程的返回值。如果不需要返回值,可以设置为 NULL

返回值:

  • 成功时返回 0

  • 失败时返回一个错误码(非零值),常见的错误码包括:

    • ESRCH: 没有找到与指定线程 ID 对应的线程。

    • EINVAL: 线程是分离的(detached)或者已经有其他线程在等待它。

    • EDEADLK: 检测到死锁(例如,线程试图等待自己)。

需要注意:

调用该函数的线程将挂起等待,直到id为thread的线程终止。

thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  • 1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  • 2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 —— PTHREAD_ CANCELED。
  • 3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  • 4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

使用示例:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>void* thread_function(void* arg) {int* value = (int*)arg;printf("Thread is running with value: %d\n", *value);int* result = (int*)malloc(sizeof(int));*result = *value * 2;pthread_exit(result);
}int main() {pthread_t thread;int value = 10;int* retval;if (pthread_create(&thread, NULL, thread_function, &value) != 0) {perror("pthread_create");exit(EXIT_FAILURE);}if (pthread_join(thread, (void**)&retval) != 0) {perror("pthread_join");exit(EXIT_FAILURE);}printf("Thread returned: %d\n", *retval);free(retval);return 0;
}

运行结果:

5.线程分离

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

注意:线程分离只是主线程不用等待新线程的退出了,并不是把新线程剥离下来了。

pthread_detach函数

功能:用于将指定的线程标记为“分离状态”,分离状态的线程在终止时会自动释放其资源,而不需要其他线程调用 pthread_join 来回收资源。

函数原型:int pthread_detach(pthread_t thread)

参数:

  • thread:要分离的线程的标识符(pthread_t 类型)

返回值:

  • 成功时返回 0

  • 失败时返回一个错误码(非零值),常见的错误码包括:

    • ESRCH: 没有找到与指定线程 ID 对应的线程。

    • EINVAL: 线程已经是分离状态,或者线程已经终止。

注意:

  • 一旦线程被分离,就不能再调用 pthread_join 来等待它,否则会导致未定义行为
  • 线程分离,可以是线程组内其他线程对目标线程进行分离,也可以是线程自己将自己分离。

使用示例:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void* thread_function(void* arg) {int* value = (int*)arg;printf("Thread is running with value: %d\n", *value);sleep(2); // 模拟线程执行一些任务printf("Thread is exiting\n");pthread_exit(NULL);
}int main() {pthread_t thread;int value = 10;if (pthread_create(&thread, NULL, thread_function, &value) != 0) {perror("pthread_create");exit(EXIT_FAILURE);}// 分离线程if (pthread_detach(thread) != 0) {perror("pthread_detach");exit(EXIT_FAILURE);}printf("Main thread continues to run...\n");sleep(3); // 确保分离的线程有足够时间完成return 0;
}

运行结果:

相关文章:

线程控制(创建、终止、等待、分离)

目录 1.前言 2.创建线程 pthread_create函数 3.线程终止 pthread_exit函数 pthread_cancel函数 4.线程等待 5.线程分离 1.前言 在Linux系统中&#xff0c;并不存在真正的线程&#xff0c;只有轻量级进程。所以&#xff0c;Linux系统只提供了操作轻量级进程的系统调用…...

【备份】php项目处理跨域请求踩坑

这都是老生常谈的东西了。我还在踩坑&#xff0c;记录一下。 我在项目入口明明写了如下代码&#xff1a; // 处理预检请求 (OPTIONS) if ($_SERVER[REQUEST_METHOD] OPTIONS) {header("Access-Control-Allow-Origin: https://xxx.vip");header("Access-Cont…...

目标检测YOLO实战应用案例100讲-面向无人机图像的小目标检测

目录 知识储备 YOLO v8无人机拍摄视角小目标检测 数据集结构 环境部署说明 安装依赖 模型训练权重和指标可视化展示 训练 YOLOv8 PyQt5 GUI 开发 主窗口代码 main_window.py 使用说明 无人机目标跟踪 一、目标跟踪的基本原理 二、常用的目标跟踪算法 基于YOLOv…...

实现 Leaflet 多类型点位标记与聚合功能的实战经验分享

在现代的地理信息系统&#xff08;GIS&#xff09;应用中&#xff0c;地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施&#xff0c;地图都能以直观的方式呈现数据。然而&#xff0c;当数据量较大时&#xff0c;地图上可能会出现大量的标记点&#x…...

Linux 环境“从零”部署 MongoDB 6.0:mongosh 安装与数据操作全攻略

前提 完成linux平台部署MongoDB【部署教程】且完成mongosh的安装 由于本人使用的是6.0版本的MongoDB&#xff0c;新版本 MongoDB&#xff08;尤其是 6.0 及以上版本&#xff09;已经不再默认捆绑传统的 mongo shell&#xff0c;而改用新的 MongoDB Shell&#xff08;mongosh&am…...

深度学习五大模型:CNN、Transformer、BERT、RNN、GAN详细解析

# 深度学习五虎将&#xff1a;当CNN遇见Transformer的奇幻漂流 ## 序章&#xff1a;AI江湖的兵器谱排行 2012年&#xff0c;多伦多大学的厨房里&#xff0c;Hinton的学生们用GPU煎了个"AlexNet"荷包蛋&#xff0c;从此开启了深度学习的热兵器时代。如今五大模型各显…...

004 rocketmq集群

1、集群模式 在RocketMQ中&#xff0c;集群的部署模式是比较多的&#xff0c;有以下几种&#xff1a; public class ConsumerDemo {public static void main(String[] args) throws Exception {DefaultMQPushConsumer consumer new DefaultMQPushConsumer("test-group&qu…...

基于 Python 深度学习的电影评论情感分析可视化系统(2.0 全新升级)

基于 Python 深度学习的电影评论情感分析可视化系统&#xff0c;基于 Flask 深度学习&#xff0c;构建了一个 影评情感分析系统&#xff0c;能够 自动分析影评、计算情感趋势 并 可视化展示&#xff0c;对于电影行业具有重要参考价值&#xff01; 基于 Python 深度学习的电影评…...

Linux内核配置与构建原理

Kconfig文件 Kconfig是Linux内核中用于配置功能的脚本语言系统&#xff0c;由众多内核源码树中每个目录下的Kconfig文件组成。它定义Linux相关的配置选项层次结构和依赖关系。 menuconfig工具&#xff0c;会抓取Kconfig中的信息&#xff0c;为用户输出友好的交互式菜单选项配…...

大语言模型微调的基本概念介绍

大型语言模型&#xff08;LLMs&#xff09;正在以惊人的速度发展&#xff0c;LLM微调的潜力更是如此。大型语言模型的生命周期有几个关键步骤&#xff0c;今天我们将要介绍这个周期中最丰富、最耗时的一部分——LLM微调过程。 大语言模型的生命周期 在深入了解大型语言模型&a…...

实例分割 | yolov11训练自己的数据集

前言 因工作要求使用的都是yolov5系列的模型&#xff0c;今天学习一下最先进的yolov11&#xff0c;记录一下环境配置及训练过程。 1.项目下载及环境安装 源码位置&#xff1a;yolov11 可以看到&#xff0c;这里要求python版本大于等于3.8&#xff0c;我这里安装python3.10.…...

vue3:四嵌套路由的实现

一、前言 1、嵌套路由的含义 嵌套路由的核心思想是&#xff1a;在某个路由的组件内部&#xff0c;可以定义子路由&#xff0c;这些子路由会渲染在父路由组件的特定位置&#xff08;通常是 <router-view> 标签所在的位置&#xff09;。通过嵌套路由&#xff0c;你可以实…...

AIGC和搜索引擎的异同

AIGC&#xff08;生成式人工智能&#xff09;与搜索引擎的核心差异体现在信息处理方式和输出形态上&#xff0c;我们可以从以下维度对比&#xff1a; 一、工作原理的本质差异 信息检索机制 搜索引擎&#xff1a;基于关键词匹配&#xff08;如"中暑怎么办"→返回相关…...

ES批量查询

在 Elasticsearch 中&#xff0c;multi_search&#xff08;也称为 msearch&#xff09;是一种允许你在单个请求中执行多个搜索操作的 API。它可以显著减少网络开销&#xff0c;尤其是在需要执行多个查询时。multi_search 会将多个查询打包成一个请求发送给 Elasticsearch&#…...

Vue2学习

一、Vue3 基础 监视属性 天气案例 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>天气案例</…...

PySide(PyQT)重新定义contextMenuEvent()实现鼠标右键弹出菜单

在 PySide中&#xff0c;contextMenuEvent() 是 QWidget 类&#xff08;以及继承自它的所有子类&#xff09;的一个事件处理方法&#xff0c;主要用于处理上下文菜单事件&#xff0c;也就是当用户在控件上右键点击时触发的事件。 • 通过重新定义contextMenuEvent()来实现自定…...

Storm实时流式计算系统(全解)——下

storm编程案例-网站访问来源实时统计-需求 storm编程-网站访问来源实时统计-代码实现 根据以上条件可以只写一个类&#xff0c;我们只需要写2个方法和一个main&#xff08;&#xff09;&#xff0c;一个读取/发射&#xff08;spout&#xff09;。 一个拿到数据统计后发到redis…...

配置Nginx日志url encode问题

文章目录 配置Nginx日志url encode问题方法1-lua方法2-set-misc-nginx-module 配置Nginx日志url encode问题 问题描述&#xff1a; 当自定义日志输出格式&#xff0c;需要输出http请求中url参数时&#xff0c;如果参数中包含中文&#xff0c;是会进行url encode的&#xff0c…...

JAVA SE 包装类和泛型

文章目录 &#x1f4d5;1. 包装类✏️1.1 基本数据类型和对应的包装类✏️1.2 装箱和拆箱✏️1.3 自动装箱和自动拆箱 &#x1f4d5;2. 泛型✏️2.1 泛型的语法✏️2.2 泛型类的使用✏️2.3 裸类型(Raw Type)✏️2.4 擦除机制✏️2.5 泛型的上界✏️2.6 泛型方法✏️2.7 通配符…...

基于Linux系统的物联网智能终端

背景 产品研发和项目研发有什么区别&#xff1f;一个令人发指的问题&#xff0c;刚开始工作时项目开发居多&#xff0c;认为项目开发和产品开发区别不大&#xff0c;待后来随着自身能力的提升&#xff0c;逐步感到要开发一个好产品还是比较难的&#xff0c;我认为项目开发的目的…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...