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

Linux 线程详解

目录

一、线程概述

二、线程创建

三、线程终止

四、线程回收

五、线程取消 

六、线程分离

七、线程安全 


一、线程概述

线程是进程内的一个执行单元,是进程内可调度的实体。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,但拥有各自独立的程序计数器、栈和寄存器等执行上下文。线程相较于进程,创建和切换开销更小,能更有效地利用多核处理器资源,提高程序的并发性能。

二、线程创建

在 Linux 中,使用 pthread_create 函数来创建线程,其函数原型如下:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg);
  • thread:指向 pthread_t 类型的指针,用于存储新创建线程的标识符。
  • attr:指向 pthread_attr_t 类型的指针,用于设置线程的属性,若为 NULL,则使用默认属性。
  • start_routine:是一个函数指针,指向线程开始执行的函数,该函数返回值为 void* 类型,参数也为 void* 类型。
  • arg:传递给 start_routine 函数的参数。

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *thread_function(void *arg) {// 线程函数,这里简单打印线程 ID 和传入的参数int num = *(int *)arg;printf("线程 ID:%lu,参数:%d\n", pthread_self(), num);pthread_exit(NULL);
}int main() {pthread_t thread_id;int num = 10;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, (void *)&num)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 主线程继续执行其他任务,这里简单睡眠一段时间sleep(2);// 等待线程结束(可选,这里只是为了确保线程在主线程退出前完成)pthread_join(thread_id, NULL);printf("主线程结束\n");return 0;
}

在上述代码中,首先定义了一个线程函数 thread_function,它接受一个 void* 类型的参数,并在线程中打印线程 ID 和传入的参数值。在 main 函数中,创建了一个线程,将 num 的地址作为参数传递给线程函数。主线程睡眠一段时间后,可以选择调用 pthread_join 等待子线程结束,确保子线程资源被正确回收后主线程再退出。 

三、线程终止

线程可以通过以下几种方式终止:

1.线程函数自然返回:线程函数执行完所有指令后返回,线程自动终止。

2.调用 pthread_exit 函数:线程可以在任何位置调用 pthread_exit 函数来显式终止自身,并可以返回一个指向线程退出状态的指针。

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

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后主动终止printf("线程即将终止\n");pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 等待线程结束(这里只是为了演示主线程的行为,实际上对于主动退出的线程,等待并非必需)pthread_join(thread_id, NULL);printf("主线程继续执行\n");return 0;
}

在上述代码中,线程函数 thread_function 执行到 pthread_exit(NULL) 时线程终止,主线程可以通过 pthread_join 来等待该线程结束,以确保线程资源被正确回收。

四、线程回收

pthread_join 函数用于回收已经终止的线程资源,并获取线程的退出状态(如果线程通过 pthread_exit 传递了退出状态)。

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

thread:要等待回收的线程 ID。

retval:用于存储线程的退出状态指针,如果不关心线程的退出状态,可以传入 NULL

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后返回一个整数值作为退出状态int *result = (int *)malloc(sizeof(int));*result = 42;pthread_exit((void *)result);
}int main() {pthread_t thread_id;void *thread_result;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 回收线程并获取退出状态if (pthread_join(thread_id, &thread_result)!= 0) {perror("线程回收失败");exit(EXIT_FAILURE);}// 输出线程的退出状态printf("线程退出状态:%d\n", *(int *)thread_result);free(thread_result);printf("主线程继续执行\n");return 0;
}

五、线程取消 

pthread_cancel 函数用于请求取消一个线程的执行,但线程必须处于可取消状态并且能够响应取消请求(线程默认是可取消的,但有些情况下可能需要修改线程的可取消性状态)。

函数原型:int pthread_cancel(pthread_t thread);

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *thread_function(void *arg) {// 线程执行一个循环,每隔一秒打印一次信息for (int i = 0; i < 10; ++i) {printf("线程运行中...\n");sleep(1);}pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 主线程睡眠 5 秒后尝试取消子线程sleep(5);if (pthread_cancel(thread_id)!= 0) {perror("线程取消失败");exit(EXIT_FAILURE);}// 回收线程(即使线程被取消,也应该进行回收以确保资源释放)pthread_join(thread_id, NULL);printf("主线程继续执行\n");return 0;
}

在上述代码中,主线程创建了一个子线程后,睡眠 5 秒,然后调用 pthread_cancel 尝试取消子线程的执行。子线程在执行过程中如果被成功取消,会立即终止,然后主线程通过 pthread_join 回收子线程资源。 

六、线程分离

线程分离是将线程设置为脱离状态,这样线程结束后其资源会自动被系统回收,而不需要其他线程调用 pthread_join 来回收。

函数原型:int pthread_detach(pthread_t thread);

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>void *thread_function(void *arg) {// 线程执行一些任务后自动终止,由于被分离,不需要其他线程回收printf("线程执行任务后将自动释放资源\n");pthread_exit(NULL);
}int main() {pthread_t thread_id;// 创建线程if (pthread_create(&thread_id, NULL, thread_function, NULL)!= 0) {perror("线程创建失败");exit(EXIT_FAILURE);}// 分离线程if (pthread_detach(thread_id)!= 0) {perror("线程分离失败");exit(EXIT_FAILURE);}// 主线程继续执行其他任务,无需等待被分离的线程printf("主线程继续执行,无需等待子线程\n");return 0;
}

在这个例子中,创建线程后立即调用 pthread_detach 将线程设置为分离状态,线程执行完任务后会自动释放资源,主线程无需调用 pthread_join 等待其结束。

七、线程安全 

线程安全是指多个线程同时访问共享资源时,不会产生数据竞争和不一致的问题。实现线程安全通常需要使用同步机制,如互斥锁、自旋锁、读写锁、条件变量等。

以下是一个使用互斥锁实现线程安全的示例,多个线程对一个共享变量进行自增操作:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>// 共享变量
int shared_variable = 0;
// 互斥锁
pthread_mutex_t mutex;void *thread_function(void *arg) {for (int i = 0; i < 10000; ++i) {// 加锁,保护共享资源pthread_mutex_lock(&mutex);shared_variable++;// 解锁pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
}int main() {pthread_t thread1, thread2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建两个线程if (pthread_create(&thread1, NULL, thread_function, NULL)!= 0) {perror("线程 1 创建失败");exit(EXIT_FAILURE);}if (pthread_create(&thread2, NULL, thread_function, NULL)!= 0) {perror("线程 2 创建失败");exit(EXIT_FAILURE);}// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("共享变量最终值:%d\n", shared_variable);return 0;
}

在上述代码中,两个线程并发地对 shared_variable 进行自增操作。通过使用互斥锁 mutex,每次只有一个线程能够进入临界区(pthread_mutex_lock 和 pthread_mutex_unlock 之间的代码段)对共享变量进行操作,从而保证了数据的一致性和线程安全。如果没有互斥锁的保护,多个线程同时对共享变量进行读写操作可能会导致数据竞争和错误的结果。 

 

相关文章:

Linux 线程详解

目录 一、线程概述 二、线程创建 三、线程终止 四、线程回收 五、线程取消 六、线程分离 七、线程安全 一、线程概述 线程是进程内的一个执行单元&#xff0c;是进程内可调度的实体。一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff0c;如内存空…...

云架构:考量与框架

云架构&#xff1a;考量与框架 引言 在当今的数字化环境中&#xff0c;云计算已成为现代商业运营的基石。一个设计良好的云架构框架为可扩展、安全和弹性的系统奠定了基础。本文将深入探讨云架构的核心要素&#xff0c;讨论重要的考量因素、设计指南&#xff0c;以及最佳实践…...

SD下载、安装、使用、卸载-Stable Diffusion整合包v4.10发布!

目录 前言概述 SD安装1、安装软件2、启动3、配置4、运行5、测试 导入SD模型【决定画风】常用模型下载安装模型 SD卸载SD文生图提示词提示词使用技巧提示词的高级使用技巧强调关键词 前言 我向来不喜欢搞一些没有用的概念&#xff0c;所以直接整理可能用到的东西。 sd简单的说…...

java 发送邮件

前期准备 pom文件中引入 JavaMail API 和 JavaBean Activation FrameWork&#xff0c;得到两个jar包&#xff1a;mail.jar 和 activation.jar 发送简单邮件&#xff08;只有邮件正文&#xff0c;普通文本&#xff09; package com.zbttest.email;import com.sun.mail.util.Ma…...

聚类系列 (二)——HDBSCAN算法详解

在进行组会汇报的时候&#xff0c;为了引出本研究动机&#xff08;论文尚未发表&#xff0c;暂不介绍&#xff09;&#xff0c;需要对DBSCAN、OPTICS、和HDBSCAN算法等进行详细介绍。在查询相关资料的时候&#xff0c;发现网络上对于DBSCAN算法的介绍非常多与细致&#xff0c;但…...

AngularJS HTML DOM

关于《AngularJS HTML DOM》的文章&#xff0c;我找到了一些有用的信息。这篇文章主要介绍了AngularJS如何通过特定的指令与HTML DOM元素进行交互。以下是一些关键点&#xff1a; ng-disabled 指令&#xff1a;这个指令用于将应用程序数据绑定到HTML的disabled属性。例如&#…...

C语言延时实现

C语言延时实现 在C语言中&#xff0c;delay 函数通过空循环实现延时&#xff0c;而不是像其他高级语言&#xff08;如Python&#xff09;直接使用 sleep 函数。这种实现方式是基于单片机的特性和C语言的底层操作。下面详细解释为什么这种空循环可以实现延时&#xff0c;以及它…...

OSI模型的网络层中产生拥塞的主要原因?

&#xff08; 1 &#xff09;缓冲区容量有限&#xff1b;&#xff08; 1.5 分&#xff09; &#xff08; 2 &#xff09;传输线路的带宽有限&#xff1b;&#xff08; 1.5 分&#xff09; &#xff08; 3 &#xff09;网络结点的处理能力有限&#xff1b;&#xff08; 1 分…...

机器学习周报-ModernTCN文献阅读

文章目录 摘要Abstract 0 提升有效感受野&#xff08;ERF&#xff09;1 相关知识1.1 标准卷积1.2 深度分离卷积&#xff08;Depthwise Convolution&#xff0c;DWConv&#xff09;1.3 逐点卷积&#xff08;Pointwise Convolution&#xff0c;PWConv&#xff09;1.4 组卷积(Grou…...

什么是网关路由

1.认识网关 网关&#xff08;Gateway&#xff09;和路由&#xff08;Router&#xff09;是两个相关但不同的概念。 一、网关&#xff08;Gateway&#xff09; 定义 网关是一个网络节点&#xff0c;它充当了不同网络之间的连接点。可以将其看作是一个网络的 “大门”&#xf…...

信号的产生、处理

一、信号的概念 信号是linux系统提供的一种&#xff0c;向指定进程发送特定事件的方式。收到信号的进程&#xff0c;要对信号做识别和处理。信号的产生是异步的&#xff0c;进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种&#xff08;用指令kill -l查看&…...

在Linux中,zabbix如何监控脑裂?

在Linux中&#xff0c;zabbix监控脑裂主要涉及对高可用&#xff08;HA&#xff09;系统中可能发生的节点间通信中断或不一致状态的监控。脑裂问题通常发生在具有冗余节点的高可用系统中&#xff0c;如集群、HA系统或分布式数据库系统&#xff0c;当节点之间失去通信时&#xff…...

C++基础概念复习

前言 本篇文章作基础复习用&#xff0c;主要是在C学习中遇到的概念总结&#xff0c;后续会继续补充。如有不足&#xff0c;请前辈指出&#xff0c;万分感谢。 1、什么是封装&#xff0c;有何优点&#xff0c;在C中如何体现封装这一特性&#xff1f; 封装是面向对象编程&…...

Earth靶场

打开靶机后使用 arp-scan -l 查询靶机 ip 我们使用 nmap 进行 dns 解析 把这两条解析添加到hosts文件中去&#xff0c;这样我们才可以访问页面 这样网站就可以正常打开 扫描ip时候我们发现443是打开的&#xff0c;扫描第二个dns解析的443端口能扫描出来一个 txt 文件 dirsear…...

JavaScript 日期格式

在 JavaScript 中,日期格式可以通过 Date 对象进行操作和格式化。下面是一些常见的 JavaScript 日期格式及其示例: 1. ISO 8601 格式 ISO 8601 是一种标准的日期和时间表示方法,格式为 YYYY-MM-DDTHH:mm:ss.sssZ,例如: let date = new Date(); console.log(date.toISOS…...

django vue3实现大文件分段续传(断点续传)

前端环境准备及目录结构&#xff1a; npm create vue 并取名为big-file-upload-fontend 通过 npm i 安装以下内容"dependencies": {"axios": "^1.7.9","element-plus": "^2.9.1","js-sha256": "^0.11.0&quo…...

xiaoya小雅超集使用夸克网盘缓存教程

距离上一次小白写到关于小雅的教程已经过去了一周的时间&#xff0c;这段时间里&#xff0c;有很多小伙伴都想知道怎么用夸克网盘作为小雅的缓存。 今天这不就来了吗&#xff1f; 这段时间确实是比较忙&#xff0c;毕竟快过年了嘛&#xff0c;辛辛苦苦一整年&#xff0c;至少…...

计算机基础知识复习1.4

数据库事务 #开启一个事务 start transaction #执行SQL语句 SQL1 SQL2 .. #提交事务 commit 类加载器 启动类加载器&#xff1a;负责加载Java的核心库 用C编写&#xff0c;是JVM的一部分&#xff0c;启动类加载器无法被Java程序直接引用 扩展类加载器&#xff1a;是Java语…...

SpringMVC(三)请求

目录 一、RequestMapping注解 1.RequestMapping的属性 实例 1.在这里创建文件&#xff0c;命名为Test: 2.复现-返回一个页面&#xff1a; 创建test界面&#xff08;随便写点什么&#xff09;&#xff1a; Test文件中编写&#xff1a; ​编辑 运行&#xff1a; 3.不返回…...

Node.js应用程序遇到了内存溢出的问题

vue 项目 跑起来&#xff0c;一直报错&#xff0c;内存溢出 在 文件node_modules 里 .bin > vue-cli-service.cmd 在依赖包这个文件第一行加上这个 node --max-old-space-size102400 "%~dp0\..\vue\cli-service\bin\vue-cli-service.js" %* node --max-old-s…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...