Linux线程同步:深度解析条件变量接口

news2024/9/20 8:14:50
🍑个人主页:Jupiter.
🚀 所属专栏:Linux从入门到进阶
欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述

目录

  • `🍑Linux线程同步`
    • `🐉条件变量---实现线程同步`
      • `💧同步概念与竞态条件`
      • `🐆条件变量接口`
            • *初始化函数*
            • *销毁条件变量*
            • *等待条件满足*
            • *唤醒等待函数*
      • *🦅为什么 pthread_cond_wait 需要互斥锁?*
          • *条件变量使用规范*


🍑Linux线程同步

🐉条件变量---实现线程同步

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。例如一个线程访问队列时,发现队列为空,它只能等待,直到到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

💧同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。
  • 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解。

🐆条件变量接口

初始化函数

可以使用预定义的宏PTHREAD_COND_INITIALIZER来静态初始化全局的pthread_cond_t变量

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

  • 参数:
    • cond:要初始化的条件变量
    • attr:NULL
销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足

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

  • 参数:
    • cond:要在这个条件变量上等待
    • mutex:互斥锁,调用 pthread_cond_wait 函数后,会自动释放持有的锁,待被唤醒后,函数调用成功返回了,会自动加锁
唤醒等待函数
  • 将指定条件变量下等待的全部线程唤醒:
    int pthread_cond_broadcast(pthread_cond_t *cond);

  • 将指定条件变量下等待的一个线程唤醒:
    int pthread_cond_signal(pthread_cond_t *cond);

🦅为什么 pthread_cond_wait 需要互斥锁?

1. 保护共享资源
在多线程环境中,多个线程可能会同时访问和修改共享资源。这些共享资源通常被互斥锁保护,以确保在任何时候只有一个线程可以访问它们。当线程调用pthread_cond_wait时,它通常位于访问这些共享资源的临界区内。因此,需要互斥锁来确保在调用pthread_cond_wait之前和之后,共享资源的状态不会被其他线程意外修改。

2. 防止虚假唤醒
pthread_cond_wait函数可能会因为多种原因被唤醒,包括真实的条件变化(由pthread_cond_signal或pthread_cond_broadcast触发)或其他系统事件(如信号或中断)。这种非条件触发的唤醒被称为“虚假唤醒”。为了避免虚假唤醒导致的错误,线程在pthread_cond_wait返回后需要再次检查条件是否真正满足。这个检查过程需要互斥锁的保护,以防止在检查条件时共享资源被其他线程修改。

3. 原子性
在调用pthread_cond_wait之前,线程需要释放互斥锁,以便其他线程可以修改条件并发出信号。然而,仅仅释放锁并调用pthread_cond_wait并不是原子的,这意味着在释放锁和进入等待状态之间可能存在时间窗口,其他线程可能会修改条件。为了解决这个问题,pthread_cond_wait函数内部会先释放锁,然后等待条件变量的信号。当条件满足且线程被唤醒时,pthread_cond_wait会重新获取之前释放的互斥锁,以确保操作的原子性

4. 防止死锁
如果在调用pthread_cond_wait时没有持有互斥锁,或者在调用后没有重新获取互斥锁,都可能导致死锁或其他同步问题。例如,如果线程在检查条件后没有立即释放锁就调用pthread_cond_wait,那么它可能会因为已经持有锁而无法被pthread_cond_signal或pthread_cond_broadcast唤醒。同样,如果线程在pthread_cond_wait返回后没有重新获取锁,那么它可能会访问一个处于不一致状态的共享资源。


按照上面的说法,我们设计出如下的代码:先上锁,发现条件不满足,解锁,然后等待在条件变量上不就行了,如下代码:

// 错误的设计 
 pthread_mutex_lock(&mutex); 
 while (condition_is_false) { 
 pthread_mutex_unlock(&mutex); 
 //解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过 
 pthread_cond_wait(&cond); 
 pthread_mutex_lock(&mutex); 
 } 
 pthread_mutex_unlock(&mutex); 
  • 由于解锁和等待不是原子操作。调用解锁之后, pthread_cond_wait 之前,如果已经有其他线程获取到互斥锁,摒弃条件满足,发送了信号,那么 pthread_cond_wait 将错过这个信号,可能会导致线程永远阻塞在这个 pthread_cond_wait 。所以解锁和等待必须是一个原子操作。
  • int pthread_cond_wait(pthread_cond_ t *cond,pthread_mutex_ t * mutex); 进入该函数后,会去看条件量等于0不?等于,就把互斥锁变成1,直到cond_ wait返回,把条件量改成1,把互斥量恢复成原样。
条件变量使用规范
  • 等待条件代码
pthread_mutex_lock(&mutex); 
 while (条件为假) 
		 pthread_cond_wait(cond, mutex); 
 //修改条件 
 pthread_mutex_unlock(&mutex);
  • 给条件发送信号代码
pthread_mutex_lock(&mutex); 
//设置条件为真 
pthread_cond_signal(cond); 
pthread_mutex_unlock(&mutex);

示例代码:

#include <unistd.h>
#include <iostream>
#include <string>
#include <pthread.h>
#include <vector>
#include <cstdlib>

using namespace std;

pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;

void *Mastercore(void *args)
{
    sleep(3);
    cout << "Master start runing ..." << endl;
    string name = static_cast<const char *>(args);

    while (true)
    {
        pthread_cond_broadcast(&gcond); //唤醒指定变量条件下等待的所有线程
        // pthread_cond_signal(&gcond); // 唤醒指定变量条件下等待的队首的线程
        cout << " Master 唤醒了一个线程 ... " << endl;
        sleep(1);
    }

    return nullptr;
}

void *SlaverCore(void *args)
{
    string name = static_cast<const char *>(args);

    while (true)
    {
        // 加锁
        pthread_mutex_lock(&gmutex);
        // 设定条件变量--这时是持有锁的,需要将锁传入,调用wait的时候,会自动解锁
        pthread_cond_wait(&gcond, &gmutex);
        cout << "wait is : " << name << endl;
        // 解锁
        pthread_mutex_unlock(&gmutex);
    }
    return nullptr;
}

void StartMaster(vector<pthread_t> *threadsptr)
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, Mastercore, (void *)"Master thread");
    if (n == 0)
    {
        cout << "Master create sucess..." << endl;
    }
    threadsptr->emplace_back(tid);
}

void StartSlaver(vector<pthread_t> *threadsptr, int n = 3)
{
    for (int i = 0; i < n; i++)
    {
        pthread_t tid;
        char *name = new char[64];   //注意需要new
        snprintf(name, 64, "Slaver-%d", i + 1);
        int n = pthread_create(&tid, nullptr, SlaverCore, (void *)name);
        if (0 == n)
        {
            cout << "create " << name << " success..." << endl;
        }

        threadsptr->emplace_back(tid);
    }
}

void WaitAll(vector<pthread_t> &threads)
{
    for (auto &t : threads)
    {
        pthread_join(t, nullptr);
    }
}

int main()
{
    vector<pthread_t> threads;
    StartMaster(&threads);
    StartSlaver(&threads, 5);

    WaitAll(threads);

    return 0;
}

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

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

相关文章

sqli-labs靶场自动化利用工具——第13关

文章目录 概要整体架构流程技术细节执行效果小结 概要 Sqli-Labs靶场对于网安专业的学生或正在学习网安的朋友来说并不陌生&#xff0c;或者说已经很熟悉。那有没有朋友想过自己开发一个测试脚本能实现自动化化测试sqli-labs呢&#xff1f;可能有些人会说不是有sqlmap&#…

每日OJ_牛客_马戏团(模拟最长上升子序列)

目录 牛客_马戏团&#xff08;模拟最长上升子序列&#xff09; 解析代码 牛客_马戏团&#xff08;模拟最长上升子序列&#xff09; 马戏团__牛客网 搜狐员工小王最近利用假期在外地旅游&#xff0c;在某个小镇碰到一个马戏团表演&#xff0c;精彩的表演结束后发现团长正和大…

《基于深度半监督学习的目标检测综述》泛读

基于深度半监督学习的目标检测方法分为 1、生成式方法 2、一致性正则化方法 3、基于图的方法 4、伪标记方法和混合方法 然后基于常用数据集 对典型方法进行了性能对比&#xff0c;最后分析了其挑战和发展趋势&#xff0c;旨在为相关研究提供参考 收获就是&#xff1a; 1…

Redis -- 全记录(面试)

目录 All : 缓存穿透 缓存击穿 互斥锁 逻辑过期 比较 : 缓存雪崩 redis怎么和数据库保持一致 双写一致性 : 延迟双删 : 保证强一致性 : 允许一定的延迟 基于mq的异步通知 基于Canal的异步通知 总结 Redis的持久化 RDB AOF 总结 Redis数据过期策略 惰性删除…

【算法专题】搜索算法

二叉树剪枝 LCR 047. 二叉树剪枝 - 力扣&#xff08;LeetCode&#xff09; 本题要求我们将全部为0的二叉树去掉&#xff0c;也就是剪枝&#xff0c;当我们举一个具体的例子进行模拟时&#xff0c;会发现&#xff0c;只关注于对其中一个子树的根节点进行剪枝&#xff0c;由于我…

Docker部署MySQL8.0.39报错解决方案

Docker部署MySQL8.0.39报错解决方案 2024-09-11T06:09:09.317582Z 0 [Warning] [MY-010139] [Server] Changed limits: max_open_files: 1024 (requested 8161) 2024-09-11T06:09:09.317586Z 0 [Warning] [MY-010142] [Server] Changed limits: table_open_cache: 431 (reques…

李彦宏内部讲话曝光,谈大模型三大认知误区:智能体还是非共识

“外界对大模型有相当多的误解&#xff0c;”近日据媒体报道&#xff0c;李彦宏的一则内部讲话曝光。在最近一次和员工交流中&#xff0c;李彦宏谈及三个大模型认知误区&#xff0c;涵盖了大模型竞争、开源模型效率、智能体趋势等热点话题。 李彦宏认为未来大模型之间的差距可…

【Axure教程】高级搜索

高级搜索可以通过使用精确的关键词或短语&#xff0c;帮助用户找到特定的内容。尤其在面对大量搜索结果时&#xff0c;通过过滤条件缩小范围&#xff0c;能够节省时间。他允许用户使用多个条件进行组合&#xff08;例如条件匹配、模糊搜索、区间筛选等&#xff09;来精准获取相…

购物车装载状态检测系统源码分享

购物车装载状态检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

瑞幸卖奶茶,霸王茶姬不慌

瑞幸和霸王茶姬&#xff0c;打不起来。 转载定焦&#xff08;dingjiaoone&#xff09;原创 作者 | 苏琦 编辑 | 魏佳 最近&#xff0c;瑞幸因为联名游戏大作《黑神话&#xff1a;悟空》无法核销套餐被骂上热搜&#xff0c;但业内人士更关注的&#xff0c;是它不久前推出的轻乳…

【网络安全】-rce漏洞-pikachu

rce漏洞包含命令执行漏洞与代码执行漏洞 文章目录 前言 什么是rce漏洞&#xff1f; 1.rce漏洞产生原因&#xff1a; 2.rce的分类&#xff1a; 命令执行漏洞&#xff1a; 命令拼接符&#xff1a; 常用函数&#xff1a; 代码执行漏洞&#xff1a; 常用函数&#xff1a; 分类&…

AI算力池化技术助力运营商打造智算生态

数字经济时代&#xff0c;算力已成为国民经济发展的重要基础设施。随着数字化转型的不断深入和人工智能技术的广泛应用&#xff0c;构建以新型智算中心为核心的智能算力生态体系正驱动着数字经济快速发展&#xff0c;成为人工智能赋能千行百业的重中之重。 2022年2月&#xff…

Vulnhub-RickdiculouslyEasy靶场(9个flag)

flag1 端口9090有一个flag flag2 13337端口 flag3 使用dirb进行扫描网站的80端口&#xff0c;发现一些敏感文件 访问80端口&#xff0c;没有发现有效信息 访问passwords目录 访问FLAG.txt 再返回访问passwords.html文件 查看页面源代码发现一个密码 flag4 之前扫描到了robo…

书接上文,介绍下Quartz Java体系结构

体系结构总结 JobDetail 我们创建一个实现 Job 接口的类&#xff0c;使用 JobBuilder 包装成 JobDetail&#xff0c;它可以携带 KV 的数据。 Trigger 定义任务的触发规律&#xff0c;Trigger&#xff0c;使用 TriggerBuilder 来构建。JobDetail 跟 Trigger 是 1:N 的关系。思…

智慧物流系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;车辆管理&#xff0c;商品管理&#xff0c;物流信息管理&#xff0c;论坛管理&#xff0c;公告信息管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品&#xff0c;论坛&#xff0c;我的 …

磁盘无法访问:深度解析与高效数据恢复策略

在数字化时代&#xff0c;磁盘作为数据存储的核心载体&#xff0c;其稳定性和可访问性直接关系到用户数据的安全与完整性。然而&#xff0c;当遇到“磁盘无法访问”的突发状况时&#xff0c;用户往往会陷入焦虑与无助之中。本文将深入探讨磁盘无法访问的原因&#xff0c;并详细…

最全面IO流介绍

1.字符集介绍 标准ASCII字符集&#xff1a;使用1个字节存储一个字符&#xff0c;首尾是0&#xff0c;总可以表示128个字符。是美国信息交换标准代码&#xff0c;包含英文、符号等等。 GBK汉字编码字符集&#xff0c;包含2万多个汉字等字符&#xff0c;GBK中一个中文字符编码成…

(Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)

接着上一篇文章来搞,批量查询点赞状态。这个接口提供给其他的微服务调用所以这里会用到FeignClient 直接上接口 1. 接口信息 这里是查询多个业务的点赞状态,因此请求参数自然是业务id的集合。由于是查询当前用戶的点赞状态,因此无需传递用戶信息。当前用户指的是登录用户 …

小琳AI课堂:LLaMA 3.1 开源大模型的全新里程碑

引言 大家好&#xff0c;这里是小琳AI课堂&#xff01;今天我们要聊的是Meta最新发布的开源大模型LLaMA 3.1。这个版本在AI界掀起了不小的波澜&#xff0c;不仅在参数规模上有显著提升&#xff0c;还在多项性能上实现了突破。让我们一起来看看LLaMA 3.1带来的新变化和意义吧&a…

UEFI基础概念2——Handle和Protocol

一、Handle Handle Database是由Handle和Protocol组成。Handle&#xff08;句柄&#xff09;是一个或多个协议的集合&#xff0c;Protocol&#xff08;协议&#xff09;是由GUID命名的数据结构。 在平台初始化过程中&#xff0c;系统固件、UEFI兼容驱动程序和UEFI应用程序创建…