Linux线程 分离和同步与互斥 条件变量

Linux线程 分离和同步与互斥 条件变量

  • 1. 分离线程
  • 2. 线程互斥与互斥量
  • 3. 线程同步与竞态条件
  • 4. pthread库与条件变量
  • 5. 生产者-消费者

1. 分离线程

  1. 什么是线程分离?
    线程分离是指线程在结束时,操作系统会自动回收其资源,而无需其他线程显式地等待它的结束或调用pthread_join函数。这种机制允许主线程不必关心子线程的状态,从而提高程序的并发性和可维护性。

  2. pthread_detach函数
    pthread_detach 函数是一个用于将线程设置为分离状态的POSIX线程库函数。通过调用这个函数,线程的资源将在其终止时自动回收,无需其他线程调用 pthread_join 来等待该线程的结束。

#include <pthread.h>
int pthread_detach(pthread_t thread);

参数说明
thread:要设置为分离状态的线程标识符
返回值
成功:返回0。
失败:返回错误码,可通过 perror 函数打印出错误信息。

代码示例:

#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 3// 线程执行的任务
void *thread_function(void *arg) {int thread_id = *((int *)arg);int n = 5;// 模拟线程执行任务,循环5次while (n--) {printf("线程 %d 正在运行\n", thread_id);sleep(1);  // 每秒打印一次,模拟线程执行任务}// 线程结束pthread_exit(NULL);
}int main() {pthread_t threads[NUM_THREADS];int thread_ids[NUM_THREADS];// 创建并启动3个线程for (int i = 0; i < NUM_THREADS; ++i) {thread_ids[i] = i + 1;// 创建线程if (pthread_create(&threads[i], NULL, thread_function, (void *)&thread_ids[i]) != 0) {perror("创建线程失败");return -1;}// 设置线程为分离状态if (pthread_detach(threads[i]) != 0) {perror("设置线程为分离状态失败");return -1;}}// 主线程执行的任务while (1) {printf("主线程正在运行\n");sleep(1);  // 主线程每秒打印一次,模拟执行其他任务}return 0;
}

运行时,使用 while :; do ps -aL ;sleep 1;done 实时查看。
在这里插入图片描述

这个程序创建了3个线程,每个线程模拟执行任务5次。主线程在一个无限循环中每秒打印一次消息,模拟执行其他任务。这里使用了 pthread_create 来创建线程,pthread_detach 将线程设置为分离状态,确保线程在结束时能够自动释放资源。

2. 线程互斥与互斥量

多线程编程是一种广泛应用于提高程序性能的技术。然而,当多个线程同时访问和修改共享资源时,可能会导致数据竞争(Data Race)和其他并发问题。为了解决这些问题,互斥量(Mutex)被引入,成为多线程编程中的重要工具。

  1. 为什么需要互斥量?
    在多线程环境下,多个线程可能同时访问和修改共享资源,导致竞态条件。竞态条件是一种并发问题,可能引起不确定的行为。为了解决这些问题,我们引入互斥量来保护共享资源,确保一次只有一个线程能够访问它

  2. 临界资源与临界区
    临界资源: 多个线程执行流共享的资源称为临界资源,它可能是一块内存、文件、网络连接等。对这些资源的并发访问可能导致不确定的结果或数据损坏。
    临界区: 是每个线程内部访问临界资源的代码段。在临界区内,对共享资源的访问需要进行互斥,以确保同一时刻只有一个线程能够进入临界区

  3. 互斥与原子性
    互斥: 互斥是一种机制,确保在任何时刻只有一个线程能够进入临界区,从而访问临界资源,通常对临界资源起保护作用。
    原子性: 表示一个操作是不可中断的,要么全部执行成功,要么完全不执行。在多线程环境中,原子操作是不会被其他线程中断的操作,确保原子性可以避免竞态条件和数据不一致性。

示例程序说明(不使用互斥量):
在这里插入图片描述
上述程序输出结果表明,由于多个线程同时访问和修改共享资源(available_tickets)而没有同步机制,导致竞态条件的发生。在输出中,票数递减,并最终变成负数。由于线程执行的顺序不确定,每次运行结果可能不同。

  1. 互斥量和加锁

在这里插入图片描述

  1. pthread_mutex_t 类型
    pthread_mutex_tPOSIX 线程库提供的互斥量类型,用于在多线程环境中同步对共享资源的访问。它是一个结构体类型,通常通过指针进行操作
  2. pthread_mutex_init 函数
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

功能: 初始化互斥量对象。
参数
mutex 是指向要初始化的互斥量对象的指针。
attr 是指向互斥量属性对象的指针。可以为 NULL,表示使用默认属性。
返回值: 若成功,返回 0;否则,返回错误码

  1. pthread_mutex_destroy 函数
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex); 

功能: 销毁互斥量对象。

参数: mutex 是指向要销毁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化一个互斥锁(mutex)。
在定义互斥量时,可以使用以下宏进行初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

功能: 使用宏 PTHREAD_MUTEX_INITIALIZER 初始化互斥量对象。

注意: 此方法只能在定义互斥量时使用,不能在运行时再次初始化。

  1. pthread_mutex_lock 函数
 #include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

功能pthread_mutex_lock 的功能是加锁,而且如果互斥量已经被锁定再次调用 pthread_mutex_lock 将会导致调用线程被阻塞,直到互斥量变为可用。

参数mutex 是指向要加锁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

  1. pthread_mutex_unlock 函数
 #include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能: 解锁。释放互斥量,使其变为可用。

参数mutex 是指向要解锁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

示例程序说明(使用互斥量):
在这里插入图片描述

pthread_mutex_t mutex 主要用于保护临界区,对共享资源进行读取或修改的那部分代码。在多线程环境中,当多个线程同时访问共享资源而缺乏适当的同步机制时,可能导致数据不一致或产生竞争条件

通过调用 pthread_mutex_lockpthread_mutex_unlock
函数,程序可以执行互斥操作。在进入临界区前,线程通过加锁确保只有一个线程能够执行临界区代码。而在退出临界区后,通过解锁释放互斥锁,使其他线程有机会进入。

以上程序通过互斥量确保了多线程环境下对共享资源(票数)的安全访问,实现了线程安全的抢票操作。

3. 线程同步与竞态条件

在多线程编程中,多个线程共享进程的资源,这样就可能导致竞态条件的产生。竞态条件是由于多个线程对共享资源的访问顺序不确定而引起的问题。为了解决这一问题,引入了线程同步的概念。

程序示例(不使用线程同步)
在这里插入图片描述

从这个输出可以看出,线程2连续抢到了多张票而其他线程并没有执行抢票的机会。这是因为在没有使用线程同步的情况下,多个线程同时访问了共享资源,导致了竞态条件。

4. pthread库与条件变量

在多线程编程中,线程之间的协调与同步是至关重要的。pthread库提供了一系列工具,其中条件变量(pthread_cond_t)是一种强大的机制。

在这里插入图片描述

  1. pthread_cond_t类型
    pthread_cond_tpthread库中用于线程同步的一种机制,也被称为条件变量条件变量允许线程在等待某个条件满足时进入休眠状态,并在条件发生变化时被唤醒。条件变量通常与互斥锁一起使用,以确保多个线程对共享资源的安全访问。

  2. pthread_cond_init函数

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

功能:
pthread_cond_init 函数用于初始化条件变量,为其分配必要的资源。

参数:
cond:指向要初始化的条件变量的指针。
attr:条件变量的属性,通常设置为 NULL 表示使用默认属性。
返回值:
若成功初始化条件变量,返回 0;否则,返回错误码。

  1. pthread_cond_destroy 函数
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);

功能:
pthread_cond_destroy 函数用于销毁条件变量,释放相关资源。

参数:
cond:指向要销毁的条件变量的指针。
返回值:
若成功销毁条件变量,返回 0;否则,返回错误码。

  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_COND_INITIALIZER宏,是一种静态初始化pthread_cond_t类型的条件变量的方式。
在定义条件变量时,可以使用以下宏进行初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  1. pthread_cond_wait函数
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

功能:
pthread_cond_wait 函数用于在等待条件变量的同时暂时释放互斥锁,使线程进入等待状态。

参数:
cond:指向要等待的条件变量的指针。
mutex:指向互斥锁的指针,用于保护对条件变量的访问。
返回值:
若成功,返回 0;否则,返回错误码。

  1. pthread_cond_broadcast函数
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);

功能:
pthread_cond_broadcast 函数用于向所有等待在条件变量上的线程发送信号,使它们从等待状态中唤醒。

参数:
cond:指向要发送信号的条件变量的指针。
返回值:
若成功,返回 0;否则,返回错误码。

  1. pthread_cond_signal函数
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);

功能
pthread_cond_signal 函数用于向等待在条件变量上的一个线程发送信号,使其从等待状态中唤醒。

参数
cond:指向要发送信号的条件变量的指针。
返回值
若成功,返回 0;否则,返回错误码。

程序示例(使用线程同步)
在这里插入图片描述

pthread_cond_init(&cond, NULL); 用于初始化条件变量 cond,与互斥锁一同使用,用于线程等待和唤醒。在线程执行函数中,通过 pthread_cond_wait(&cond, &mutex); 进行等待,释放互斥锁 mutex 进入阻塞状态。

主线程通过 pthread_cond_signal(&cond); 发送信号,唤醒一个等待条件变量的线程,被唤醒的线程在获取互斥锁之前不能继续执行。这确保了每个线程在唤醒后都能够争夺到互斥锁,避免了线程饥饿的情况。

5. 生产者-消费者

  1. 生产者消费者模型的概念

在这里插入图片描述

生产者消费者模型通过中间容器(阻塞队列)解决了生产者和消费者强耦合的问题。两者不直接通讯,而是通过阻塞队列传递数据。生产者产生数据后,无需等待消费者处理,直接放入阻塞队列。消费者不主动请求数据,而是直接从阻塞队列取出,阻塞队列充当了缓冲区的角色。

  1. 生产者、消费者之间的关系

在生产者消费者模型中存在三种关系:

生产者和生产者之间的互斥关系: 确保不同生产者之间的操作互斥,以保证数据的正确生成。

消费者和消费者之间的互斥关系: 确保不同消费者之间的操作互斥,防止并发访问导致问题。

生产者和消费者之间的互斥与同步关系: 实现互斥以保证读写安全,同时在缓冲区数据满或空时,确保能够互相等待和通知,实现同步操作。生产者线程和消费者线程是两种不同的角色,彼此之间通过缓冲区进行数据交流,这个缓冲区可被看作是一个特定结构的交换平台。

代码示例:

在这里插入图片描述

pthread_mutex_t mutex:互斥锁

作用: 保护对缓冲区的访问,确保在同一时刻只有一个线程能够访问或修改共享资源
使用场景: 在生产者和消费者访问缓冲区时,通过互斥锁进行加锁和解锁,避免多个线程同时修改共享资源导致数据不一致或冲突。


pthread_cond_t not_full:缓冲区不满的条件变量
作用: 用于通知生产者线程缓冲区不再是满的状态,可以继续生产数据。
使用场景: 当缓冲区满时,生产者线程通过等待该条件变量进入阻塞状态,直到消费者线程通知它缓冲区不再是满的。

pthread_cond_t not_empty:缓冲区不空的条件变量
作用: 用于通知消费者线程缓冲区不再是空的状态,可以继续消费数据。
使用场景: 当缓冲区为空时,消费者线程通过等待该条件变量进入阻塞状态,直到生产者线程通知它缓冲区不再是空的。

这个程序展示了基本的生产者-消费者模型,通过互斥锁和条件变量确保线程安全的访问共享资源。生产者不断生成随机数作为数据项并放入缓冲区,而消费者则从缓冲区中取出数据项进行消费。这种模型确保了在多线程环境中的安全数据传递和协同工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/675677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java:内部类、枚举、泛型以及常用API --黑马笔记

内部类 内部类是类中的五大成分之一&#xff08;成员变量、方法、构造器、内部类、代码块&#xff09;&#xff0c;如果一个类定义在另一个类的内部&#xff0c;这个类就是内部类。 当一个类的内部&#xff0c;包含一个完整的事物&#xff0c;且这个事物没有必要单独设计时&a…

[职场] 进入大数据领域需要掌握哪些软件 #其他#职场发展#职场发展

进入大数据领域需要掌握哪些软件 学习大数据首先我们要学习Java语言和Linux操作系统&#xff0c;这两个是学习大数据的基础&#xff0c;学习的顺序不分前后。 Java 大家都知道Java的方向有JavaSE、JavaEE、JavaME&#xff0c;学习大数据要学习那个方向呢? 只需要学习Java的…

2024 年 6 款值得推荐的 iOS 系统恢复软件

iPhone 以其时尚的设计、用户友好的界面和强大的性能而闻名。然而&#xff0c;没有任何技术能够避免错误和故障&#xff0c;iPhone 也不例外。作为 iPhone 用户&#xff0c;您可能遇到过各种可能导致设备故障的问题。虽然 Apple 提供了多种解决方案来解决这些问题&#xff0c;但…

算法练习-二叉搜索树中的搜索(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;二叉树 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨…

Linux系统安装(CentOS Vmware)

学习环境安装 VMware安装 VMware下载&安装 访问官网&#xff1a;https://www.vmware.com 在此处可以选择语言 点击China&#xff08;简体中文&#xff09; 点击产品&#xff0c;点击Workstation Pro 下滑&#xff0c;点击下载试用版 下滑找到Workstation 17 Pro for Wi…

Linux开发:PAM1 介绍

PAM(Pluggable Authentication Modules )是Linux提供的一种通用的认证方式,他可以根据需要动态的加载认证模块,从而减少认证开发的工作量以及提供认证的灵活度。 1.PAM的框架 PAM的框架由一下几个部分构成 1)应用程序,即需要使用认证服务的程序,这些应用程序是使用抽象…

【大厂AI课学习笔记】1.5 AI技术领域(6)目标检测

目标检测是CV中的重要场景。 在图像中定位感兴趣的目标&#xff0c;准确判断每个目标的类别&#xff0c;并给出每个目标的边界框。 上图是目标检测的典型应用案例。 目标检测的难点是小目标的高精度检测。 目前主要的应用领域是机器人导航、自动驾驶、智能视频监督、工业检测…

fast table行的自动扩大

首先选择行 选择表格或其任何元素&#xff0c;并将光标置于所需行的左侧。光标的形式将变为一个黑色小箭头&#xff1a; 单击鼠标左键&#xff0c;以选择行&#xff1b; 右击鼠标&#xff0c;显示该行的右键菜单 最好的行分断 第 [ToInt32([Page])-4] 页

K8S之标签的介绍和使用

标签 标签定义标签实操1、对Node节点打标签2、对Pod资源打标签查看资源标签删除资源标签 标签定义 标签就是一对 key/value &#xff0c;被关联到对象上。 标签的使用让我们能够表示出对象的特点&#xff0c;比如使用在Pod上&#xff0c;能一眼看出这个Pod是干什么的。也可以用…

基于SpringBoot和PostGIS的震中影响范围可视化实践

目录 前言 一、基础数据 1、地震基础信息 2、全国行政村 二、Java后台服务设计 1、实体类设计 2、Mapper类设计 3、控制器设计 三、前端展示 1、初始化图例 2、震中位置及影响范围标记 3、行政村点查询及标记 总结 前言 地震等自然灾害目前还是依然不能进行准确的预…

小明与光明之剑:勇者的试炼

现在&#xff0c;我将根据这些步骤编写一个对话形式的童话故事。 很久很久以前&#xff0c;在一个遥远的国度里&#xff0c;有一个勇敢的小男孩叫小明。有一天&#xff0c;他得知自己的村庄正遭受一只凶猛的巨龙侵袭&#xff0c;许多村民都受到了伤害。 小明决定踏上征程&#…

Java面向对象 多态

目录 多态多态的好处实例创建一个Main 多态 在Java中&#xff0c;多态是面向对象编程的三大基本特性之一&#xff0c;另外两个是封装和继承。多态是指一个接口可以有多种实现方式&#xff0c;或者一个对象可以表现出多种形态。 在Java中&#xff0c;多态主要通过方法重载和重写…

新增同步管理、操作日志模块,支持公共链接分享,DataEase开源数据可视化分析平台v2.3.0发布

2024年2月5日&#xff0c;DataEase开源数据可视化分析平台正式发布v2.3.0版本。 这一版本的功能升级包括&#xff1a;新增“同步管理”功能模块&#xff0c;用户可通过此模块&#xff0c;将传统数据库中的数据定时同步到Apache Doris中&#xff0c;让数据分析更快速&#xff1…

寒假思维训练day19

更新一道div3的F 和 做出来过的一道类似这个F的 icpc铜牌题, 美赛以后的第一篇。 题目链接&#xff0c;有需自取&#xff1a; div3 F链接&#xff1a;Problem - F - Codeforces icpc Asia macau 铜牌题 Problem - K - Codeforces 摘要 Part1 div3 F 的题意、题解、代码…

SolidWorks学习笔记——草图绘制的基本命令

目录 一、进入草图绘制 二、直线命令与删除命令 三、圆弧命令与矩形命令 四、槽口命令以及多边形命令 五、椭圆以及倒角命令 六、草图绘制中的剪裁命令 七、草图中的几何关系 八、草图绘制中的智能尺寸 九、从外部粘贴草图&#xff08;CAD&#xff09; 一、进入草图绘…

Node.js之npm单独与批量升级依赖包的方式

Node.js之npm单独与批量升级依赖包的方式 文章目录 Node.js之npm单独与批量升级依赖包的方式npm查看与升级依赖包1. 单独安装或升级最新版本2. 查看依赖但不升级1. npm outdated2. npm update 3. 批量升级新版本4. npm-check-updates1. 全局安装2. ncu查看可升级的版本3. 升级依…

MATLAB实现LSTM时间序列预测

LSTM模型可以在一定程度上学习和预测非平稳的时间序列,其具有强大的记忆和非线性建模能力,可以捕捉到时间序列中的复杂模式和趋势[4]。在这种情况下,LSTM模型可能会自动学习到时间序列的非平稳性,并在预测中进行适当的调整。其作为循环神经网络(RNN)的特殊形式,继承了循…

项目02《游戏-09-开发》Unity3D

基于 项目02《游戏-08-开发》Unity3D &#xff0c; 本次任务是做抽卡界面&#xff0c;获取的卡片增添在背包中&#xff0c;并在背包中可以删除卡片&#xff0c; 首先在Canvas下创建一个空物体&#xff0c;命名为LotteryPanel&#xff0c;作为抽卡界面&#xff0c; …

MyBatis之动态代理实现增删改查以及MyBatis-config.xml中读取DB信息文件和SQL中JavaBean别名配置

MyBatis之环境搭建以及实现增删改查 前言实现步骤1. 编写MyBatis-config.xml配置文件2. 编写Mapper.xml文件&#xff08;增删改查SQL文&#xff09;3. 定义PeronMapper接口4. 编写测试类1. 执行步骤2. 代码实例3. 运行log 开发环境构造图总结 前言 上一篇文章&#xff0c;我们…

PySpark(三)RDD持久化、共享变量、Spark内核制度,Spark Shuffle、Spark执行流程

目录 RDD持久化 RDD 的数据是过程数据 RDD 缓存 RDD CheckPoint 共享变量 广播变量 累加器 Spark 内核调度 DAG DAG 的宽窄依赖和阶段划分 内存迭代计算 Spark是怎么做内存计算的? DAG的作用?Stage阶段划分的作用? Spark为什么比MapReduce快&#xff1f; Spa…