【Linux —— 线程同步 - 条件变量】

news2024/12/25 12:56:38

Linux —— 线程同步 - 条件变量

  • 条件变量的概念
  • 互斥量与条件变量的关系
  • 条件变量的操作
  • 代码示例

条件变量的概念

条件变量是一种用于线程间同步的机制,主要用于协调线程之间的执行顺序,允许线程在某个条件不满足时进入等待状态,直到其他线程通知条件已满足。条件变量通常与互斥锁一起使用,以确保在检查和修改共享状态时的安全性。

主要功能:

  • 线程等待:当某个条件不满足时,线程可以在条件变量上等待。
  • 线程通知:当条件满足时,其他线程可以通知等待的线程继续执行。

条件变量的核心作用是提供一种机制,使得线程可以在条件未满足时挂起自己,并在条件满足时被唤醒。
这样可以避免忙等待(busy waiting)和不必要的 CPU 资源浪费。

互斥量与条件变量的关系

  互斥量和条件变量在多线程编程中密切相关,通常结合使用以实现更加复杂的同步机制。他们之间的关系可以概括为一下几点:

  1. 互斥量保护条件变量
     条件变量的操作必须在互斥量的保护之下进行,以确保对共享资源的安全访问。线程在调用pthread_cond_wait()等待条件变量时,必须先获取相关的互斥变量锁。这样可以防止其他线程在条件检查和修改期间访问共享资源。(对共享区资源的检查也是一种访问)

  2. 等待与通知机制
     当线程调用pthread_cond_wait()来进入等待状态时,互斥量会被自动解锁,允许其他线程修改共享资源和条件状态。其他线程在改变条件后,会使用pthread_cond_signal()pthread_cond_broadcast() 来通知等待的线程,这需要在持有互斥量的情况下使用。

  3. 重新检查条件
     被唤醒的线程在继续执行之前,必须重新获取互斥量并检查条件是否满足。这是因为在它被唤醒
    之前,其他线程可能已经改变了条件状态。线程不能假设条件一定满足,而是需要重新验证。

  4. 互斥量的状态影响条件变量的使用
     互斥量的状态(锁定或解锁)直接影响到线程是否能够在条件变量上等待或被唤醒。只有在互斥
    量被解锁的情况下,其他线程才能访问和修改条件变量的相关条件。

  5. 组合使用
     互斥量和条件变量通常一起使用来解决复杂的同步问题,例如生产者-消费者问题、读者-写者问
    题等。它们的组合使用能够有效地协调线程之间的执行顺序,避免竞争条件和资源浪费。

总之,互斥量和条件变量是多线程编程中不可或缺的同步机制。互斥量确保同一时刻只有一个线
程可以访问共享资源,而条件变量则提供了一种线程等待和通知的机制。通过将这两种机制结合
使用,程序员可以编写出更加复杂和高效的多线程程序。

关键点总结:


  • 互斥量的自动解锁:当线程在条件变量上等待时,互斥量会被自动解锁,这使得其他线程可以修改共享资源。
  • 条件的重新检查:被唤醒的线程在继续执行之前必须重新获取互斥量并检查条件,以确保条件的有效性。
  • 互斥量的状态影响条件变量的使用:互斥量的状态(锁定或解锁)直接影响到线程是否能够在条件变量上等待或被唤醒。

条件变量的操作

条件变量的操作主要包括:

  • 等待(Wait):当线程发现条件不满足时,它会调用 pthread_cond_wait() 函数进入等待状态。在调用该函数时,线程会自动释放与条件变量关联的互斥量,并进入阻塞状态,等待其他线程的通知。
  • 通知(Signal):当条件满足时,其他线程可以调用pthread_cond_signal() pthread_cond_broadcast() 来唤醒一个或多个等待该条件变量的线程。

1.条件变量的初始化:

函数原型:

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

参数

  • cond: 指向要初始化的条件变量的指针。
  • attr: 指向条件变量属性对象的指针。如果设置为 NULL,则使用默认属性。

返回值

  • 成功时返回 0。
  • 失败时返回错误编号,例如:
    • EINVAL: 参数无效。
    • ENOMEM: 内存不足。

2.条件变量的销毁

函数原型:

int pthread_cond_destroy(pthread_cond_t *cond);

参数

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

返回值

  • 成功时返回 0。
  • 失败时返回错误编号,例如:
    • EBUSY: 条件变量上仍有线程在等待。
    • EINVAL: 参数无效。
  1. 等待条件变量

函数原型:

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

参数

  • cond: 指向条件变量的指针。
  • mutex: 指向与条件变量关联的互斥量的指针。
    返回值
  • 成功时返回 0。
  • 失败时返回错误编号,例如:
    • EINVAL: 参数无效。
    • ESRCH: 线程不存在。

为什么要在pthread_cond_wait中传入互斥锁?


pthread_cond_wait() 中传入互斥锁是为了确保线程安全。当线程调用该函数时,互斥锁会被自动解锁,允许其他线程访问和修改共享资源。线程在等待条件变量时,必须持有互斥锁,以防止竞争条件,并在被唤醒后重新获取互斥锁,以确保对共享资源的安全访问。

  1. 唤醒条件变量

函数原型:

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

参数

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

返回值

  • 成功时返回 0。
  • 失败时返回错误编号,例如:
    • EINVAL: 参数无效。

使用说明:
pthread_cond_signal():唤醒一个等待该条件变量的线程。如果没有线程在等待,则不执行任何操作。 pthread_cond_broadcast():唤醒所有等待该条件变量的线程。

代码示例

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

const int num = 2;
int n = 4;
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;

void* Wait(void* args)
{
    std::string name = *static_cast<std::string*>(args);
    delete static_cast<std::string*>(args); // 释放动态分配的内存

    int m = 2;

    while (m--)
    {
        pthread_mutex_lock(&g_mutex);
        pthread_cond_wait(&g_cond, &g_mutex);
        std::cout << "I am: " << name << " ..." << std::endl;
        pthread_mutex_unlock(&g_mutex);
    }

    return nullptr;
}

int main()
{
    pthread_t threads[num];

    for (int i = 0; i < num; i++)
    {
        auto name = new std::string("thread-" + std::to_string(i + 1));
        if (pthread_create(&threads[i], nullptr, Wait, name) != 0) {
            std::cerr << "Failed to create thread " << i + 1 << std::endl;
            delete name; // 确保内存被释放
            return 1;
        }
    }

    sleep(1);

    while (n--)
    {
        pthread_mutex_lock(&g_mutex); // 锁住互斥量
        pthread_cond_signal(&g_cond);
        std::cout << "唤醒了一个线程" << std::endl;
        pthread_mutex_unlock(&g_mutex); // 解锁互斥量
        sleep(2);
    }

    for (int i = 0; i < num; i++)
    {
        pthread_join(threads[i], nullptr);
    }

    // 销毁互斥量和条件变量
    pthread_mutex_destroy(&g_mutex);
    pthread_cond_destroy(&g_cond);

    return 0;
}

在这里插入图片描述

这段代码实现了一个简单的多线程程序,其中创建了两个线程,它们在条件变量上等待,直到主线程发出信号唤醒它们。代码使用了互斥量来确保对共享资源的安全访问,并使用条件变量来实现线程之间的同步

  1. 在 main 函数中,首先创建了两个线程,并指定它们要执行的函数为 Wait。每个线程都有一个唯一的名称,用于标识它们。

  2. 创建线程时,使用了 new 动态分配内存来存储线程名称。主线程在创建所有线程后睡眠 1 秒钟,以确保所有线程都已创建并处于等待状态。

  3. 然后,主线程进入循环,共执行 4 次。在每次循环中,它都会调用pthread_cond_signal函数来唤醒一个等待的线程。在唤醒线程之后,主线程会打印一条消息,表示已经唤醒了一个线程,并睡眠 2 秒钟。

  4. 在主线程唤醒所有线程之后,它会等待所有线程完成执行。这是通过调用 pthread_join 函数来实现的。当所有线程都完成执行后,main 函数返回 0,表示程序成功退出。

  5. Wait 函数中,每个线程都会尝试获取互斥量 g_mutex 。一旦获取到互斥量,线程就会调用 pthread_cond_wait 函数在条件变量 g_cond 上等待。 这里是引用
    这里的关键点是:

    1. 获取互斥量:
      • 在进入 while 循环之前,线程会调用 pthread_mutex_lock(&g_mutex) 来获取互斥量g_mutex
      • 这确保了在访问共享资源和条件变量时,只有一个线程可以进入临界区。
    2. 等待条件变量:
      • 获取互斥量后,线程会立即调用 pthread_cond_wait(&g_cond, &g_mutex)
      • pthread_cond_wait() 函数有两个参数:条件变量 g_cond 和互斥量 g_mutex
      • 当线程调用这个函数时,会发生以下事情:
        • 线程会自动释放互斥量 g_mutex。这允许其他线程在此期间获取互斥量并进入临界区。
        • 线程会被放入条件变量 g_cond 的等待队列,并进入阻塞状态。
    3. 被唤醒后:
      • 当其他线程调用 pthread_cond_signal()pthread_cond_broadcast()来唤醒等待的线程时,被唤醒的线程会重新获取互斥量 g_mutex
      • 这确保了在继续执行之前,线程可以安全地访问共享资源。

    所以,在 Wait 函数中,每个线程首先获取互斥量,然后调用 pthread_cond_wait()在条件变量上等待。这个过程确保了线程在访问共享资源之前拥有互斥量,并且只有在条件满足时才会继续执行。

  6. 在调用pthread_cond_wait时,线程会自动释放互斥量 g_mutex。当线程被唤醒时,它会重新获取互斥量 g_mutex。一旦获取到互斥量,线程就会打印一条消息,表示它已经被唤醒。在打印消息之后,线程会释放互斥量 g_mutex。这个过程会重复两次。

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

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

相关文章

【Linux I/O】万字长文带思维导图,一文彻底掌握Linux I/O:深入解析操作系统数据交互的艺术

Linux I/O Linux I/O&#xff08;输入/输出&#xff09;是操作系统与外部设备进行数据交互的过程。在Linux系统中&#xff0c;I/O操作的管理和优化对于系统性能有着至关重要的影响。本文将详细介绍Linux中的各种I/O模型&#xff0c;包括它们的工作原理、优缺点以及适用场景&am…

ImportError: DLL load failed while importing _ssl: 找不到指定的模块。

windonw cmd下的输出&#xff1a; (python3.9) PS D:\git\ImageAnalysisService\core\medical_bills> python Python 3.9.19 (main, May 6 2024, 20:12:36) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or …

计算机基础知识总结(八股文--计算机网络、操作系统、数据库、c++、数据结构与算法)

一、操作系统 0.内存管理 01.什么是虚拟内存&#xff1f;为什么需要虚拟内存&#xff1f; 虚拟内存为程序提供比实际物理内存更大的内存空间&#xff0c;同时提高内存管理的灵活性和系统的多任务处理能力。虚拟地址空间就是进程所能看到的内存空间&#xff0c;这段空间是连续…

苍穹外卖项目DAY11

苍穹外卖项目DAY11 1、Apache ECharts 1.1、介绍 Apache ECharts是一款基于JavaScript的数据可视化图标库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图标 官网&#xff1a;Apache ECharts 1.3、入门案例 <!DOCTYPE…

LlamaIndex 实现 RAG(四)- RAG 跟踪监控

RAG 整个流程不复杂&#xff0c;集成三大部分包括文档解析并生成向量、根据查询问题查找语意相似的数据文档块、把查询问题和召回文档作为上下文的数据传给模型进行解答。大语言模型的应用开发和传统的开发方式区别很大&#xff0c;以前开发完成&#xff0c;只要逻辑正确&#…

解决IDEA 控制台中文乱码及无法输入中文

一、IDEA 控制台中文乱码&#xff1a; 问题描述&#xff1a; IntelliJ IDEA 如果不进行相关设置&#xff0c;可能会导致控制台中文乱码、配置文件中文乱码等问题。 解决方案&#xff1a; ①&#xff1a;设置字体为支持中文的字体&#xff1a; 点击菜单 File - > settings …

二分查找【算法 09】

二分查找算法详解 二分查找&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;前提是数据必须是有序的。相比于线性查找&#xff0c;二分查找的时间复杂度从 O(n) 降低到了 O(log n)&#xff0c;适合处理大规模的数据查找问题。本文将详细介绍二分查找的原…

Catf1ag CTF Crypto(六)

前言 Catf1agCTF 是一个面向所有CTF&#xff08;Capture The Flag&#xff09;爱好者的综合训练平台&#xff0c;尤其适合新手学习和提升技能 。该平台由catf1ag团队打造&#xff0c;拥有超过200个原创题目&#xff0c;题目设计注重知识点的掌握&#xff0c;旨在帮助新手掌握C…

集团数字化转型方案(十六)

为了全面推进集团的数字化转型&#xff0c;我们将实施一系列战略举措&#xff0c;包括整合最新的人工智能、大数据分析和云计算技术&#xff0c;升级企业资源规划&#xff08;ERP&#xff09;系统&#xff0c;实现业务流程的自动化与优化&#xff1b;同时&#xff0c;建立全方位…

计算机是如何工作的(2)

文章目录 一. 寄存器和存储器二. 操作系统二. 进程PCB1. pid2. 内存指针3. 文件描述符表4. 属性1) 状态2) 优先级3) 上下文4) 记账信息 一. 寄存器和存储器 存储器是内存和硬盘的通称 内存, 存储空间比硬盘小, 速度比硬盘快, 价格比硬盘高, 掉电后数据流失寄存器是CPU上的一个…

ORACLE EBS R12系统的安装及维护案例

引言&#xff1a; Oracle E-Business Suite (EBS) R12 是企业中广泛应用的一体化管理解决方案&#xff0c;涵盖了财务、人力资源、供应链等多个业务领域。以下将详细介绍如何在 Windows 系统上安装 Oracle EBS R12&#xff0c;并分享一些日常维护的技巧和最佳实践。 点击下载…

基于imx6ull平台opencv的图像采集、ffmpeg推流和Windows端拉流(多线程)

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 验证功能一、概述 本文档是针对imx6ull平台opencv的图像采集、ffmpeg推流和Windows端拉流。首先创建一个线程opencv通过摄像头采集视频图像,接着再创建两个线程,其中一个线程获取采集的视频图…

Python编码系列—Python中的HTTPS与加密技术:构建安全的网络通信

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

驾驭高效编程:一探C++ STL的奥秘

1.什么是STL 2.:STL的版本 2.1:原始版本 2.2:P.J版本 2.3:RW版本 2.4:SGI版本 3:STL的六大组件 4:如何学习STL 5:STL的缺陷 1.什么是STL STL(standdard template library-标准模板库):是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包含数据结构与算法软…

MJJ 必备自建 IDC 系统 WHMCS 开心版 圆你一个老板梦

本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 WHMCS 是我们买 VPS 的最常见到的 IDC 系统,最近为了写 VPS 库存监控脚本,自己搭了一个 WHMCS 用来测试,顺便分享一下如何搭建属于自己的 WHMCS,圆你一个老板梦。WHMCS 开心版仅限学习、开发使用,商业用途请前往…

Linux内核学习之中断处理

Linux内核学习之中断处理 0 前言1 中断处理程序的嵌套执行1 Linux对x86异常的处理Linux中向量用途1 Linux中的中断门描述符Linux中的中断描述符硬中断软中断和tasklet软中断tasklet[^2]ksoftirqd内核线程kworker内核线程 0 前言 文本基于x86架构讲解Linux中对中断的处理&#…

Telink泰凌微如何添加Lib库

基于TLSR8258 SDK&#xff1a;b85m_ble_single_connection_sdk 1.首先把lib文件放在sdk的proj_lib文件夹中 2. properties - c/c build - Settings - TC32 C Linker - Libraries&#xff0c;把文件添加到编译设置中。注意添加的库文件名需要删除“lib前缀”。例如&#xff1…

外贸管理软件一般都有哪些功能

外贸管理软件通常被设计来帮助国际贸易企业高效管理其业务流程。这类软件的功能多样&#xff0c;这里以神卓外贸管理软件为例&#xff0c; 以下是一些常见的核心功能模块&#xff1a; 客户关系管理 (CRM) 客户信息管理询盘与报价管理销售机会跟踪 订单管理 订单生成与处理发货…

Codeforces Round 916 (Div. 3) E1. Game with Marbles(博弈论*1400)

感觉很难想。 如果你直接想的话&#xff0c;你就会发现有很多做法可以选择&#xff0c;而你根本不知道应该选哪个。 这时候可以先假设鲍勃已经取走了爱丽丝的所有的颜色的弹珠&#xff0c;&#xff08;并且以每个颜色一个弹珠的代价&#xff09;。 这时候每一项得分就是 S i …

Linux简单介绍(2)

四、软件管理机制 4.1 Linux软件管理介绍 有一个很好的软件生态圈支持&#xff0c;才是一个优秀、值得广泛使用的操作系统平台。比如PC端的window操作系统、mac操作系统&#xff0c;手机端的IOS系统&#xff0c;Android系统等。在这些操作系统上安装软件&#xff0c;方便的不能…