深入理解多线程
一、线程基本概念
1、概述
线程是允许应用程序并发的一种机制。线程共享进程内的所有资源。
线程是调度的基本单位。
每个线程都有自己的 errno。
所有 pthread 函数均以返回 0 表示成功,返回一个正值表示失败。
编译 pthread 程序需要添加链接库(-lpthread)。
线程的主要优势在于,能够通过全局变量来共享信息。同时也引入一个问题,多个线程对临界资源的竞争。
2、线程终止方式
1、线程函数执行 return 语句并返回指定值。
2、线程调用 pthread_exit。
3、调用 pthread_cancel() 取消线程。
4、任意线程调用 exit(), 或者主线程执行 return 语句。
二、Pthreads 数据类型

三、线程接口
1、创建线程
#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
pthread_create() 创建线程通过调用带有 arg 参数的 start_routine 函数开始执行。
2、终止线程
#include <pthread.h>void pthread_exit(void *retval);
3、线程 ID
#include <pthread.h>// 获取当前线程 ID
pthread_t pthread_self(void);// 判断 2 个线程ID是否相等
int pthread_equal(pthread_t t1, pthread_t t2);
4、连接已终止的线程
#include <pthread.h>int pthread_join(pthread_t thread, void **retval);
pthread_join 等待由 thread 标识的线程终止。
如果 pthread_join 传入一个之前已经连接过的线程 ID,将导致无法预知的行为。
默认情况下,线程是可连接的(join),也就是程序退出时,其他线程可以通过调用 pthread_join() 获取其返回状态。
5、线程分离
#include <pthread.h>int pthread_detach(pthread_t thread);
有时,我们不关心程序的返回状态,只是希望系统在线程终止时能够自动清理并移除。在这种情况下,可以调用 pthread_detach 并向 thread 参数传入指定线程的标识符,将该线程标记为处于分离状态。
线程可以通过调用 pthread_detach() 实现自行分离。
一旦线程处于分离状态,就不能再使用 pthread_join() 获取其状态,也无法使其重返可连接状态。
6、线程取消
#include <pthread.h>int pthread_cancel(pthread_t thread);
7、线程可取消性检查
#include <pthread.h>void pthread_testcancel(void);
8、清理函数
#include <pthread.h>void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
9、向线程发送信号
#include <signal.h>int pthread_kill(pthread_t thread, int sig);
#include <signal.h>
#include <pthread.h>int pthread_sigqueue(pthread_t thread, int sig,const union sigval value);
10、操作线程信号掩码
#include <signal.h>int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
四、线程属性
五、线程同步 - 保护共享变量的访问:互斥量
1、分配互斥量
1、静态分配
pthread_mutex_t mutex;
2、动态分配
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2、销毁互斥量
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);
3、加锁和解锁互斥量
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
调用 pthread_mutex_lock() 锁定互斥量。如果互斥量当前处于未锁定状态,该调用将锁定互斥量并立即返回。如果互斥量处于锁定状态,pthread_mutex_lock() 调用会一直阻塞,直到该互斥量被解锁。
pthread_mutex_unlock() 解锁调用线程锁定的互斥量,以下行为均为错误:
1、对未锁定的互斥量进行解锁。
2、解锁其他线程锁定的互斥量。
4、互斥量死锁
5、互斥量属性
6、互斥量类型
1、PTHREAD_MUTEX_NORMAL
不具备死锁自检功能。
线程试图对已由自己锁定的互斥量加锁,则发生死锁。
互斥量处于未锁定状态,或由其他线程锁定,对其解锁会导致不确定的结果。
2、PTHREAD_MUTEX_ERRORCHECK
此类互斥量的所有操作都会执行错误检查。
可以作为调试工具,以发现程序在哪里违反了互斥量使用的基本原则。
3、PTHREAD_MUTEX_RECURSIVE
该互斥量维护一个锁计数器。当线程第一次取得互斥量时,会将锁定计数器置1,后续同一线程的每次加锁操作会递增锁定计数器的数值,而解锁操作则会递减计数器计数,只有锁计数器值降至0时,才会释放。
六、线程同步 - 通知状态的改变:条件变量
条件变量允许一个线程就某个共享变量(或其他共享资源)的状态变化通知其他线程。
条件变量总是结合互斥量使用。
所有线程都应该处理虚假的唤醒。
条件变量并不保存状态信息,只是传递应用程序状态信息的一种通讯机制。发送信号时,若无任何线程在等待条件变量,这个信号也就不了了之。
1、分配条件变量
1、静态分配
pthread_cond_t cond;
2、动态分配
#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2、销毁条件变量
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);
3、通知条件变量
#include <pthread.h>// 至少唤起一个线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤起所有阻塞线程
int pthread_cond_broadcast(pthread_cond_t *cond);
4、等待条件变量
#include <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
七、线程安全
1、可重入性
要诀:避免使用全局变量和静态变量。
2、一次性初始化
#include <pthread.h>int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;
3、线程特有数据
4、线程局部存储
附录一:多线程示例
1、main.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* pthread_message_function(void *ptr);int main(int argc, char *argv[])
{pthread_t pthreadID1,pthreadID2;int ret = 0;char *message1 = "thread1";char *message2 = "thread2";/* 1、创建线程1 */ret = pthread_create(&pthreadID1,NULL,pthread_message_function,(void*)message1);if(ret != 0){printf("%s create fail!\r\n",message1);}else{printf("%s create sucess!\r\n",message1);}/* 2、创建线程2 */ret = pthread_create(&pthreadID2,NULL,pthread_message_function,(void*)message2);if(ret != 0){printf("%s create fail!\r\n",message2);}else{printf("%s create sucess!\r\n",message2);}/* 3、休眠一定时间,等待子线程结束 */sleep(10);printf("main thread exit\r\n");return 0;
}void* pthread_message_function(void *ptr)
{int i = 0;/* 分离线程 - 避免僵尸线程产生 */pthread_detach(pthread_self());/* 业务逻辑 */for (i; i<5; i++) {printf("%s thread: %d\n", (char *)ptr, i);sleep(1);}
}
2、makefile
a.out: main.cgcc main.c -o a.out -lpthread.PHONY : clean
clean:rm -rf a.out
3、测试
[root@localhost pthread]# make
gcc main.c -o a.out -lpthread
[root@localhost pthread]# ls
a.out main.c makefile
[root@localhost pthread]# ./a.out
thread1 create sucess!
thread2 create sucess!
thread2 thread: 0
thread1 thread: 0
thread2 thread: 1
thread1 thread: 1
thread2 thread: 2
thread1 thread: 2
thread2 thread: 3
thread1 thread: 3
thread2 thread: 4
thread1 thread: 4
main thread exit
[root@localhost pthread]#
相关文章:
深入理解多线程
一、线程基本概念 1、概述 线程是允许应用程序并发的一种机制。线程共享进程内的所有资源。 线程是调度的基本单位。 每个线程都有自己的 errno。 所有 pthread 函数均以返回 0 表示成功,返回一个正值表示失败。 编译 pthread 程序需要添加链接库(…...
华为OD机试题 - 英文输入法(JavaScript)
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解: 英文输入法题目输入输出示例一输入输出说明示例一输入输出Code…...
64 云原生容器化
文章目录 一、什么是rancher二、为什么使用rancher三、 Rancher与[k8s](https://so.csdn.net/so/search?q=k8s&spm=1001.2101.3001.7020)的关系及区别1、Rancher具有的优势三、rancher安装1、细部介绍四、图形化操作1、执行2、图形化操作1、进行客户机登录rancher2、Ranch…...
IronXL for .NET 2023.2.5 Crack
关于适用于 .NET 的 IronXL 在 C# 中阅读和编辑 Excel 电子表格,无需 MS Office 或 Excel Interop。 IronXL for .NET 允许开发人员在 .NET 应用程序和网站中读取、生成和编辑 Excel(和其他电子表格文件)。您可以读取和编辑 XLS/XLSX/CSV/TS…...
计算机组成原理|第一章(笔记)
目录第一章 计算机系统概论1.1 计算机系统简介1.1.1 计算机的软硬件概念1.1.2 计算机系统的层次结构1.1.3 计算机组成和计算机体系结构1.2 计算机的基本组成1.2.1 冯 诺伊曼计算机的特点1.2.2 计算机的硬件框图1.2.3 计算机的工作过程1.3 计算机硬件的主要技术指标1.3.1 机器字…...
[ vulnhub靶机通关篇 ] Empire Breakout 通关详解
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…...
IP定位离线库有什么作用?
IP离线是什么意思?我们以丢失手机为例来寻找它,现在手机都有IP定位功能,只要手机开通了IP定位,就能找到手机。iPhone定位显示离线一般是iPhone手机关机了或者iPhone手机中“查找我的iPhone”功能关闭了。如果手机在手中的话可以打…...
[C++]vector模拟实现
目录 前言: 1. vector结构 2. 默认成员函数 2.1 构造函数 无参构造: 有参构造: 有参构造重载: 2.2 赋值运算符重载、拷贝构造(难点) 2.3 析构函数: 3. 扩容 3.1 reserve 3.2 resize…...
DevOps实战50讲-(2)Jenkins配置
1. Docker镜像方式安装拉取Jenkins镜像docker pull jenkins/jenkins编写docker-compose.ymlversion: "3.1" services:jenkins:image: jenkins/jenkinscontainer_name: jenkinsports:- 8080:8080- 50000:50000volumes:- ./data/:/var/jenkins_home/首次启动会因为数据…...
LC-1599. 经营摩天轮的最大利润(贪心)
1599. 经营摩天轮的最大利润 难度中等39 你正在经营一座摩天轮,该摩天轮共有 4 个座舱 ,每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱,但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。…...
Umi使用百度地图服务
需求描述 需要在前端页面中使用地图定位功能,所以在前端umi项目中使用百度地图服务,由于umi项目默认没有入口的html文件,所以无法通过常规的在head中加入外链js的方式使用 百度ak zyqeLCzvQPCCNImRu9yRGOqWlEUicxxGreact使用百度api 链接:…...
js中getBoundingClientRect()方法
getBoundingClientRect()返回值是一个 DOMRect 对象,是包含整个元素的最小矩形(包括 padding 和 border-width)。该对象使用 left、top、right、bottom、x、y、width 和 height 这几个以像素为单位的只读属性描述整个矩形的位置和大小。除了 …...
Unity记录2.2-动作-动画、相机、Debug与总结
文章首发及后续更新:https://mwhls.top/4453.html,无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评,非常感谢! 汇总:Unity 记录 摘要:重写了动画触…...
分享十个前端Web3D可视化框架附地址
Three.js:Three.js是一个流行的3D库,提供了大量的3D功能,包括基本几何形状、材质、灯光、动画、特效等。它是一个功能强大、易于使用的框架,广泛用于Web3D可视化应用程序的开发。Three.js:https://threejs.org/Babylon…...
基于WSL2和Clion搭建Win下C开发环境
系列文章目录 一、基于WSL2和Clion搭建Win下C开发环境 二、make、makeFile、CMake、CMakeLists的使用 三、全面、详细、通俗易懂的C语言语法和标准库 文章目录系列文章目录前言WSL2安装WSL常用命令VSCode连接WSLroot密码以systemd启动配置sshClion结语前言 Win下C语言开发环境…...
考研第一天,汤家凤基础班,连续与极限复习笔记
函数连续极限性质保号性证明极值点:夹逼准则二项式展开根号下,大于一,小于一的讨论直接放缩求和分子分母齐次,且分母大一次,用积分单调有界存在极限几个重要的切线放缩证明有界,然后放缩求单调证明有界&…...
聊一聊代码重构——关于变量的代码实践
提炼变量 其目标是将一个复杂表达式或语句分解成更小的部分,并将其存储在变量中。提高代码可读性和复用性 复杂的表达式 有些时候为了方便我们会把业务处理的逻辑写在一起,如果参与处理的内容较多时我们就会创造出一个非常长且难以理解的表达式。当其他…...
Spring之基于注解方式实例化BeanDefinition(1)
最近开始读Spring源码,读着读着发现里面还是有很多很好玩的东西在里面的,里面涉及到了大量的设计模式以及各种PostProcessor注入的过程,很好玩,也很复杂,本文就是记录一下我学习过程中的主干流程。 在开始我们源码解读…...
【STM32】入门(十四):FreeRTOS-任务
1、简述 FreeRTOS应用程序由一组独立的任务构成。 在任何时间点,应用程序中只能执行一个任务,FreeRTOS调度器负责决定所要执行的任务。 每个任务在自己的上下文中执行,不依赖于系统内的其他任务或 FreeRTOS的调度器本身。 FreeRTOS调度器负责…...
apscheduler 的基本介绍和使用
APScheduler有四大组件: 1、触发器 triggers : 触发器包含调度逻辑。每个作业都有自己的触发器,用于确定下一个任务何时运行。除了初始配置之外,触发器是完全无状态的。 有三种内建的trigger: (1)date: 特定…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
