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

【Linux】多线程:线程同步、条件变量

目录

一、同步的概念

为什么需要同步呢?

二、条件变量

条件变量的相关概念

1、条件变量的初始化:静态初始化、动态初始化 

2、条件变量的等待:pthread_cond_wait函数

工作原理及流程【重要!】

关键点总结

3、条件变量的激发(通知)


一、同步的概念

此处的同步的概念和我们生活中所理解的同步并不是一回事。我们常说的同步,其意义更贴近于在在同一时间内同时执行多个任务,实际上,这在进程/线程调度的理念中叫做并行。

进程/线程间同步实际上所指的是协调多个线程或进程的执行顺序,以确保它们以一致和正确的方式访问共享资源。即:在保证数据安全的前提下, 让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。

为什么需要同步呢?

  • 避免竞争条件: 当多个线程同时访问共享资源时,如果没有适当的同步机制,可能会出现竞争条件,导致不确定的结果。同步确保每次只有一个线程能访问共享资源。

  • 确保数据一致性: 多线程操作可能导致共享数据处于不一致的状态。同步机制确保数据在访问期间保持一致性。

  • 协调线程的执行: 同步用于控制线程的执行顺序和状态,确保线程在适当的时刻执行特定操作。

二、条件变量

在 Linux 中,条件变量(condition variable)是一个用于线程间同步的机制。它允许线程在某些条件满足之前挂起执行,并在条件满足时被唤醒。

条件变量的相关概念

  1. 条件变量(condition variable): 是一种同步机制,通常与互斥锁(mutex)一起使用。它用于实现线程间的等待和通知机制。

  2. 等待队列: 当线程在条件变量上调用 pthread_cond_wait 函数时,它会释放相关的互斥锁进入一个等待队列中此时线程会被挂起,直到另一个线程通过条件变量的 pthread_cond_signal 或 pthread_cond_broadcast 函数唤醒它。

  3. 互斥锁: 条件变量的使用必须与互斥锁配合使用,以确保在检查条件和改变条件时的原子性。

1、条件变量的初始化:静态初始化、动态初始化 

 静态初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

解释:
这是静态初始化条件变量的方式。PTHREAD_COND_INITIALIZER 是一个宏,用于初始化条件变量 cond。该宏定义在 POSIX 线程库中,适用于在编译时已知的全局或静态条件变量。

优点:

  • 不需要额外的初始化函数调用。
  • 不需要手动销毁条件变量,生命周期随程序。

限制:

  • 仅适用于静态或全局条件变量的初始化。
  • 不能用于动态创建的条件变量。

动态初始化

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

参数:

  • cond:指向要初始化的条件变量的指针。该条件变量必须在使用之前被初始化。
  • attr:用于指定条件变量的属性。可以为 NULL,表示使用默认属性;也可以指定一个 pthread_condattr_t 结构体来设置自定义属性。

返回值:

  • 如果初始化成功,返回 0
  • 如果初始化失败,返回一个错误代码。

优点:

  • 适用于动态分配的条件变量。
  • 可以通过 attr 参数设置条件变量的特定属性。

使用示例:

pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

在使用动态分配方法时,需要在不再需要条件变量时调用 pthread_cond_destroy 来销毁它,以释放相关资源!!!

2、条件变量的等待:pthread_cond_wait函数

条件变量的等待和通知机制依赖于互斥量(mutex),确保在检查和修改条件时的原子性。

函数原型

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

参数:

  • cond:指向条件变量的指针。
  • mutex:指向互斥量的指针。

返回值:

  • 成功时,返回 0
  • 失败时,返回一个错误代码(例如,EINVAL 或 EPERM)。
工作原理及流程【重要!】
  1. 持有互斥量: 调用 pthread_cond_wait 前,线程必须首先持有与条件变量相关联的互斥锁。这个互斥量保护了条件变量的状态和与之相关的数据。

  2. 挂起线程: 线程在 pthread_cond_wait 被调用时,会自动释放互斥锁,并进入等待队列中将自己挂起(阻塞)。这使得其他线程可以在条件变量上进行信号操作。

  3. 条件满足: 当条件满足时(通常由其他线程调用 pthread_cond_signal 或 pthread_cond_broadcast),挂起的线程会被唤醒。唤醒后,线程会重新竞争互斥锁,竞争成功后恢复执行。

  4. 检查条件: 唤醒后的线程需要重新检查条件是否满足。如果条件未满足,线程可能会再次调用 pthread_cond_wait 以继续挂起。通常,这种情况在一个 while 循环中处理,以应对虚假唤醒。

示例代码

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int condition = 0;void* waiting_thread(void* arg) {pthread_mutex_lock(&mutex);while (condition == 0) {printf("Waiting for condition...\n");pthread_cond_wait(&cond, &mutex);}printf("Condition met! Exiting...\n");pthread_mutex_unlock(&mutex);return NULL;
}void* signaling_thread(void* arg) {sleep(2); // Simulate some workpthread_mutex_lock(&mutex);condition = 1;pthread_cond_signal(&cond);printf("Condition signaled.\n");pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t waiter, signaler;pthread_create(&waiter, NULL, waiting_thread, NULL);pthread_create(&signaler, NULL, signaling_thread, NULL);pthread_join(waiter, NULL);pthread_join(signaler, NULL);pthread_cond_destroy(&cond);pthread_mutex_destroy(&mutex);return 0;
}
关键点总结
  1. 互斥量管理: 调用 pthread_cond_wait 之前,必须先锁定互斥量。条件变量在内部会自动解锁互斥量并挂起线程,然后在条件满足后再重新锁定互斥量。

  2. 条件检查: 使用 pthread_cond_wait 后,需要在 while 循环中检查条件,以处理可能的虚假唤醒(假设线程被唤醒时条件并未真正满足)。

  3. 虚假唤醒: pthread_cond_wait 可能会由于虚假唤醒而返回,即使条件尚未满足。因此,条件变量的等待通常放在 while 循环中,以确保条件的正确性。

  4. 通知机制: 其他线程可以使用 pthread_cond_signal 唤醒一个等待中的线程,或使用 pthread_cond_broadcast 唤醒所有等待中的线程。

  5. 销毁资源: 在使用完条件变量和互斥量后,应该调用 pthread_cond_destroy 和 pthread_mutex_destroy 来释放资源。

3、条件变量的激发(通知)

3.1、一次唤醒单个线程的函数:pthread_cond_signal

函数原型

int pthread_cond_signal(pthread_cond_t *cond);

参数:

  • cond:指向条件变量的指针。

返回值:

  • 成功时,返回 0
  • 失败时,返回一个错误代码(例如,EINVAL 或 EPERM)。

工作原理

  1. 持有互斥量: 调用 pthread_cond_signal 前,线程必须首先持有与条件变量相关联的互斥量。这个互斥量保护了条件变量的状态和与之相关的数据。

  2. 唤醒线程: 当 pthread_cond_signal 被调用时,至少有一个挂起的线程(等待条件变量)被唤醒(解除阻塞)。如果多个线程在等待同一个条件变量,通常会选择一个线程来唤醒。被唤醒的线程尝试重新获得互斥量以便继续执行。

  3. 继续执行: 被唤醒的线程退出 pthread_cond_wait 函数,并继续执行。一般地,它会重新检查它等待的条件,以确保它真的被激发的,而不仅仅是虚假唤醒。

3.2、一次唤醒全部线程的函数:pthread_cond_signal 

pthread_cond_broadcast 是 POSIX 线程库(pthread)中的一个函数,用于激发条件变量上所有等待的线程。与 pthread_cond_signal 不同,pthread_cond_broadcast 会唤醒所有等待在指定条件变量上的线程,而不仅仅是一个线程。这在某些情况下非常有用,例如:当条件变化会影响多个线程时。

函数原型

int pthread_cond_broadcast(pthread_cond_t *cond);

参数:

  • cond:指向条件变量的指针。

返回值:

  • 成功时,返回 0
  • 失败时,返回一个错误代码(例如,EINVAL 或 EPERM)。

工作原理

  1. 持有互斥量: 在调用 pthread_cond_broadcast 之前,线程必须持有与条件变量相关联的互斥量。这是为了保护条件变量及其相关的数据,并确保状态的原子性。

  2. 唤醒所有线程: 当 pthread_cond_broadcast 被调用时,它会唤醒所有在该条件变量上等待的线程。被唤醒的线程会尝试重新获得互斥量,然后继续执行。

  3. 重新检查条件: 被唤醒的线程会退出 pthread_cond_wait,但通常会重新检查它所等待的条件。如果条件未满足,它可能会再次调用 pthread_cond_wait,继续等待。这样做是为了处理虚假唤醒的情况。

 3.3、需要注意的点!!!

  • 虚假唤醒: 即便条件不满足,pthread_cond_wait 也可能因为虚假唤醒而返回。因此,依赖于条件变量等待的代码经常在 while 循环中检查条件,以确保线程确实被因为条件成立而唤醒:
pthread_mutex_t mutex;
pthread_cond_t cond;
int condition;// ...void wait_for_condition() {while (!condition) {pthread_cond_wait(&cond, &mutex);}// condition 现在为真,执行其他工作...
}

3.4、对比总结

  • pthread_cond_signal

    • 唤醒一个等待线程。
    • 适用于只需要通知一个线程的场景。
    • 可能更高效,尤其是在只需要唤醒一个线程的情况下,减少了不必要的线程唤醒开销。
  • pthread_cond_broadcast

    • 唤醒所有等待线程。
    • 适用于需要通知所有等待线程的场景。
    • 确保所有等待线程都能接收到条件变化,从而做出适当的响应。

 要真正的理解同步,我们还需借助代码来理解。下一节我们将介绍使用互斥锁与条件变量实现的生产者消费者模型。

相关文章:

【Linux】多线程:线程同步、条件变量

目录 一、同步的概念 为什么需要同步呢&#xff1f; 二、条件变量 条件变量的相关概念 1、条件变量的初始化&#xff1a;静态初始化、动态初始化 2、条件变量的等待&#xff1a;pthread_cond_wait函数 工作原理及流程【重要&#xff01;】 关键点总结 3、条件变量的激…...

【Android Studio】使用雷电模拟器调试

文章目录 进入开发者模式使雷电模拟器adb连接PC测试 进入开发者模式 多次点击版本号 -开区USB调试 使雷电模拟器adb连接PC 写cmd脚本 雷电模拟器端口为5555 &#xff0c;脚本内容如下&#xff1a; adb.exe connect 127.0.0.1:5555双击bat脚本文件 测试...

你必须知道的C语言问题(9)

问&#xff1a;如下代码&#xff0c;两个结构体类型成员变量相同&#xff0c;只是成员顺序不同&#xff0c;为什么大小不同&#xff1f; #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h>typedef struct _test1{uint…...

如何通过网络找到自己想要的LabVIEW知识?

学习LabVIEW或其他编程技术时&#xff0c;无法依赖某一篇文章解决所有问题。重要的是通过多种途径获取灵感&#xff0c;并学会归纳总结&#xff0c;从而逐渐形成系统性的理解。这种持续学习和总结的过程是技术提升的基础。通过网络找到所需的LabVIEW知识可以通过以下几个步骤进…...

SCRM电商管理后台Axure高保真原型 源文件

在电商行业蓬勃发展的今天&#xff0c;企业急需一个全面的客户关系管理&#xff08;CRM&#xff09;系统来优化他们的电商运营。我们的Scrm电商管理后台应运而生&#xff0c;它不仅是一个集中化的管理平台&#xff0c;更是企业提升客户互动和销售业绩的得力助手。 预览地址 ht…...

重载new,delete , RTTI,类成员指针

重载new&#xff0c;delete 执行过程 重载new&#xff0c;delete 和普通的运算符重载不同&#xff0c;并非重载new&#xff0c;delete 的行为&#xff0c;而是改变内存分配的方式&#xff0c;将对象放置在特定的内存空间中 new运算符操作&#xff1a; 调用STL标准模板库的…...

基于SSM+Vue+MySQL的在线医疗服务系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着医疗信息化的快速发展和患者对便捷医疗服务需求的日益增长&#xff0c;开发一个高效、可靠的在线医疗服务系统显得尤为重要。基于SSM&#xff08;SpringSpring MVCMyBatis&#xff09;框架、前端采用Vue.js、后端连接MySQL数…...

Windows 11上pip报‘TLS/SSL connection has been closed (EOF) (_ssl.c:1135)‘的解决方法

这个只是简单记录一下&#xff0c;可能是我用了代理的缘故&#xff0c;即便是把源换成国内的&#xff0c;例如阿里云&#xff0c;也会报错&#xff0c;例如&#xff1a; pip install matplotlib Looking in indexes: https://mirrors.aliyun.com/pypi/simple/, https://pypi.or…...

53 - I. 在排序数组中查找数字 I

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9853%20-%20I.%20%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E6%95%B0%E5%AD%97%20I/README.md 面试题 53 - I. 在排序数组中查找数字 …...

基于 TDMQ for Apache Pulsar 的跨地域复制实践

导语 自2024年9月6日起&#xff0c;TDMQ Pulsar 版专业集群支持消息、元数据两级跨地域复制功能&#xff0c;消息级复制解决用户全球地域的数据统一归档问题&#xff0c;元数据级复制提供解决用户核心业务跨地域容灾的场景。 用户在跨地域场景遇到的疑问和挑战 在跨地域相关…...

无线通信感知/雷达系统算法专业技术栈

无论是在工业界还是在学业界&#xff0c;无线通信感知一体化都是一个热门的方向&#xff0c;作为一个24届毕业生&#xff0c;刚好处于行业当中&#xff0c;就总结一下自己浅薄认知下&#xff0c;自己觉得已经掌握或者应该掌握的技术栈和专业能力&#xff0c;与大家共勉。 Rada…...

离谱碾压!奇安信中标:高出第二名近70分!

2024年08月09日&#xff0c;广东省政务服务和数据管理局&#xff0c;近日发布了网络安全第三方服务&#xff08;2024年&#xff09;项目之关基检查及重要政务应用安全检查服务招标公告&#xff01; 预算金额&#xff1a;2,896,200.00元&#xff0c;其中安全检查服务包&#xf…...

HOT 100(七)栈、堆、贪心算法

一、栈 1、每日温度 使用单调递减栈来解决。主要思路是遍历temperatures数组&#xff0c;利用栈来存储还没有找到比当前温度高的天数的索引。当遇到比栈顶索引所对应温度更高的温度时&#xff0c;就可以确定当前这一天的温度比之前那一天高。索引的差值就是等待的天数。 求一…...

速盾:高防服务器租用需要注意什么事项

在当今互联网时代&#xff0c;网络安全问题日益严峻。各种网络攻击手段层出不穷&#xff0c;给企业和个人的网站带来了巨大的安全威胁。为了保障网站的安全稳定运行&#xff0c;高防服务器成为了许多人的选择。而在租用高防服务器时&#xff0c;需要注意以下几个事项。 一、选择…...

【数据库】MySQL内置函数

本篇分享一些在MySQL中常见的一些内置函数&#xff0c;如日期函数&#xff0c;字符串函数和数学函数&#xff0c;以方便于操作数据库中的数据。 1.日期函数 我们先整体观察一下这些函数再讲解案例 日期函数使用起来都非常就简单 获得年月日&#xff1a; select current_dat…...

Promise查漏及回调地狱结构优化

目录 前言一、执行器函数的执行顺序二、如何在then()中抛出错误三、期约的"非重入"特性四、串行化期约五、应对回调地狱结语 前言 依据《JavaScript高级程序设计》对Promise期约相关进行查缺补漏. 一、执行器函数的执行顺序 执行器函数虽作为期约的参数, 却是期约的…...

SpringCloud-05 Resilience4J 服务降级和熔断

服务雪崩&#xff1a;指在多个服务之间存在依赖关系时&#xff0c;当一个服务发生故障或不可用时&#xff0c;导致其他服务也无法正常工作的情况。这种现象通常是因为服务之间的依赖关系过于紧密&#xff0c;当一个服务发生故障时&#xff0c;其他服务无法正确处理该服务的请求…...

哈希表、算法

哈希表 hash&#xff1a; 在编程和数据结构中&#xff0c;"hash" 通常指的是哈希函数&#xff0c;它是一种算法&#xff0c;用于将数据&#xff08;通常是字符 串&#xff09;映射到一个固定大小的数字&#xff08;哈希值&#xff09;。哈希函数在哈希表中尤为重要…...

变更AWS EC2 实例配置或实例类型

在本文中&#xff0c;九河云将带您了解如何更改由 Amazon Elastic Block Store (EBS) 支持的 Amazon Elastic Compute Cloud (EC2) 实例的类型。更改实例类型可以优化成本和性能&#xff0c;使其更符合您的应用程序需求。 准备工作 在开始之前&#xff0c;请确保您已完成以下…...

【前端】ref引用的作用

首先&#xff0c;我们要明确一点&#xff0c;使用vue的好处是&#xff1a; 想要减少开发者直接操作dom元素。使用组件模版&#xff0c;实现代码的服用。 ref的属性的实现是为了取代原生js中使用id、class等标识来获取dom元素。 helloworld组件 <template><div clas…...

运用Java实现倒计时功能

这个功能其实是比较好实现的&#xff0c;一般来说java中实现倒计时有两种方法&#xff1a; 1、使用 scheduledexecutorservice创建一个可重复执行的任务&#xff0c;直到时间到&#xff1a; ScheduledExecutorService 是 Java 中一种用于安排延迟或定期任务的工具。我们可以使…...

Vue 第三方调用若依系统实现系统单点登录

应用场景 甲方现有平台系统拟集成我方新开发系统&#xff0c;实现单点登录功能&#xff0c;即用户登录主平台后&#xff0c;无需重复登录即可无缝访问新系统&#xff0c;提升用户体验与操作效率。 解决方案 实现代码 前端 Step:1 新建ssoLogin.vue页面 <template><d…...

IP纯净度对跨境电商有哪些影响

在全球化贸易的浪潮中&#xff0c;跨境电商凭借其打破地理界限的能力&#xff0c;成为推动国际贸易的重要力量。然而&#xff0c;跨境电商的运营并非没有挑战&#xff0c;其中IP纯净度是影响其成功的关键因素之一。本文将探讨IP纯净度对跨境电商运营的多方面影响&#xff0c;并…...

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用&#xff0c;大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境&#xff0c;确保代码的环境一致性&#xff0c;从而不会在出现这段代码在我机器上没问…...

国产视频转换HDMI1.4转单/双MIPI DSI/CSI LT6911C芯片方案,带音频输出,QFN64封装 Lontium

LT6911C:HDMI 1.4 TO MIPI DSI/CSI 芯片简介&#xff1a; LT6911C是一款高性能的HDMI1.4转换器MIPI DSI/CSI芯片用于VR/智能手机/显示应用。对于MIPI DSI/CSI输出&#xff0c;LT6911C功能可配置单端口或双端口MIPIDSI/CSI 1高速时钟通道和1~4个高速数据通道最大1.5Gb/s/lane&am…...

亚马逊、沃尔玛、敦煌网、Target塔吉特、Temu环境搭建测评技术!

海外跨境电商各大主要平台正不断力推半托管模式&#xff0c;不断对商家开出众多吸引和扶持政策。全托管是指电商平台全面负责店铺的运营&#xff0c;包括仓储、配送、售后等&#xff0c;而商家主要负责提供货品。半托管模式则基本由商家自主经营&#xff0c;平台只负责仓配物流…...

yjs05——matplotlib画其他图像

不管是折线图还是散点图&#xff0c;饼状图&#xff0c;柱状图等&#xff0c;其流程都是 1.创建幕布 ❤2.画图画坐标补充信息 3.保存图像 4.展示图像 不同就是在画图时候的代码不太相同 折线&#xff1a;plt.plot(x,y) 散点&#xff1a;plt.scatter() 柱状图&#xff1a;plt.hi…...

【C#】添加临时环境变量

在C#中&#xff0c;可以通过System.Environment类来添加临时环境变量。临时环境变量只在当前进程中有效&#xff0c;进程结束后变量即失效&#xff0c;不会写入系统的Path中。 using System;class Program {static void Main(){// 设置临时环境变量Environment.SetEnvironment…...

物联网之ESP32与微信小程序实现指示灯、转向灯

MENU ESP32微信小程序 ESP32 代码 #include <WiFi.h> #include <WebServer.h> #include <ArduinoJson.h>const char* ssid "jifu"; const char* pass "2022xinchan!#"; const int dateTime 500; const int ledPin4 4; const int le…...

ICPC网络赛 以及ACM训练总结

一、训练反思 关于我自己暑假期间训练的反思&#xff0c;我承认无论是因为什么原因&#xff0c;我自己浪费我整整一个暑假的时间&#xff0c;暑假期间正是我们集训的关键时期&#xff0c;这期间没有任何的事情来打扰我们学习&#xff0c;而我却熬夜&#xff0c;白天训练懈怠&a…...