当前位置: 首页 > 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;我认为项目开发的目的…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...