【CI130x 离在线】音频下发时条件变量的使用

背景

基于Linux C++,有这么两个线程,一个线程收到从机请求数据了的信号,另一个线程等待这个信号,并将音频下发。

假如是两个类(这两个线程分属于两个不同的cpp/h 文件),那么条件变量如何使用?

 

 

正文

当条件变量需要在两个不同的类(分属不同的cpp/h文件)中使用时,需要仔细设计接口和共享状态的管理。我来提供几种解决方案:

方案1:使用全局条件变量和互斥量

shared_resources.h

cpp
#ifndef SHARED_RESOURCES_H
#define SHARED_RESOURCES_H

#include <mutex>
#include <condition_variable>

// 声明全局的同步原语
extern std::mutex g_audio_mutex;
extern std::condition_variable g_audio_cv;
extern bool g_data_requested;

#endif

shared_resources.cpp

cpp
#include "shared_resources.h"

// 定义全局的同步原语
std::mutex g_audio_mutex;
std::condition_variable g_audio_cv;
bool g_data_requested = false;

request_receiver.h

cpp
#ifndef REQUEST_RECEIVER_H
#define REQUEST_RECEIVER_H

#include <thread>
#include <atomic>

class RequestReceiver {
private:
    std::thread receiver_thread_;
    std::atomic<bool> running_;

public:
    RequestReceiver();
    ~RequestReceiver();
    
    void start();
    void stop();
    
private:
    void runReceiver();
};

#endif

request_receiver.cpp

cpp
#include "request_receiver.h"
#include "shared_resources.h"
#include <iostream>
#include <chrono>

RequestReceiver::RequestReceiver() : running_(false) {}

RequestReceiver::~RequestReceiver() {
    stop();
}

void RequestReceiver::start() {
    if (!running_) {
        running_ = true;
        receiver_thread_ = std::thread(&RequestReceiver::runReceiver, this);
        std::cout << "请求接收器启动" << std::endl;
    }
}

void RequestReceiver::stop() {
    if (running_) {
        running_ = false;
        if (receiver_thread_.joinable()) {
            receiver_thread_.join();
        }
        std::cout << "请求接收器停止" << std::endl;
    }
}

void RequestReceiver::runReceiver() {
    while (running_) {
        // 模拟接收外部请求
        std::this_thread::sleep_for(std::chrono::seconds(3));
        
        if (!running_) break;
        
        {
            std::lock_guard<std::mutex> lock(g_audio_mutex);
            g_data_requested = true;
            std::cout << "[" << std::this_thread::get_id() 
                      << "] RequestReceiver: 收到从机数据请求" << std::endl;
        }
        
        // 通知音频发送器
        g_audio_cv.notify_one();
    }
}

audio_sender.h

cpp
#ifndef AUDIO_SENDER_H
#define AUDIO_SENDER_H

#include <thread>
#include <atomic>
#include <vector>

class AudioSender {
private:
    std::thread sender_thread_;
    std::atomic<bool> running_;

public:
    AudioSender();
    ~AudioSender();
    
    void start();
    void stop();
    
private:
    void runSender();
    std::vector<char> generateAudioData();
    void sendAudioData(const std::vector<char>& data);
};

#endif

audio_sender.cpp

cpp
#include "audio_sender.h"
#include "shared_resources.h"
#include <iostream>
#include <chrono>
#include <cstring>

AudioSender::AudioSender() : running_(false) {}

AudioSender::~AudioSender() {
    stop();
}

void AudioSender::start() {
    if (!running_) {
        running_ = true;
        sender_thread_ = std::thread(&AudioSender::runSender, this);
        std::cout << "音频发送器启动" << std::endl;
    }
}

void AudioSender::stop() {
    if (running_) {
        running_ = false;
        // 通知等待的线程退出
        g_audio_cv.notify_one();
        if (sender_thread_.joinable()) {
            sender_thread_.join();
        }
        std::cout << "音频发送器停止" << std::endl;
    }
}

void AudioSender::runSender() {
    while (running_) {
        std::unique_lock<std::mutex> lock(g_audio_mutex);
        
        // 等待数据请求或停止信号
        g_audio_cv.wait(lock, [this]() {
            return g_data_requested || !running_;
        });
        
        if (!running_) break;
        
        // 重置请求标志
        g_data_requested = false;
        lock.unlock();
        
        // 生成并发送音频数据
        auto audio_data = generateAudioData();
        sendAudioData(audio_data);
    }
}

std::vector<char> AudioSender::generateAudioData() {
    std::vector<char> audio_data(1024);
    static int counter = 0;
    std::string audio_info = "AudioBlock_" + std::to_string(counter++);
    memcpy(audio_data.data(), audio_info.c_str(), 
           std::min(audio_info.length(), audio_data.size()));
    return audio_data;
}

void AudioSender::sendAudioData(const std::vector<char>& data) {
    std::cout << "[" << std::this_thread::get_id() 
              << "] AudioSender: 开始下发音频数据, 大小: " 
              << data.size() << " bytes" << std::endl;
    
    // 模拟发送过程
    for (int i = 0; i < 3; ++i) {
        std::cout << "[" << std::this_thread::get_id() 
                  << "] 发送数据块 " << i + 1 << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    std::cout << "[" << std::this_thread::get_id() 
              << "] AudioSender: 音频数据下发完成" << std::endl;
}

方案2:使用管理器类(推荐)

audio_manager.h

cpp
#ifndef AUDIO_MANAGER_H
#define AUDIO_MANAGER_H

#include <mutex>
#include <condition_variable>
#include <atomic>

class AudioManager {
private:
    std::mutex mtx_;
    std::condition_variable cv_;
    std::atomic<bool> data_requested_;
    std::atomic<bool> shutdown_;

    AudioManager(); // 私有构造函数,单例模式

public:
    // 删除拷贝构造函数和赋值运算符
    AudioManager(const AudioManager&) = delete;
    AudioManager& operator=(const AudioManager&) = delete;
    
    // 单例访问点
    static AudioManager& getInstance();
    
    // 请求相关方法
    void signalDataRequest();
    void waitForRequest();
    bool checkRequest();
    
    // 生命周期管理
    void shutdown();
    bool isShutdown() const;
};

#endif

audio_manager.cpp

cpp
#include "audio_manager.h"
#include <iostream>

AudioManager::AudioManager() 
    : data_requested_(false), shutdown_(false) {}

AudioManager& AudioManager::getInstance() {
    static AudioManager instance;
    return instance;
}

void AudioManager::signalDataRequest() {
    {
        std::lock_guard<std::mutex> lock(mtx_);
        data_requested_ = true;
        std::cout << "AudioManager: 数据请求信号已设置" << std::endl;
    }
    cv_.notify_one();
}

void AudioManager::waitForRequest() {
    std::unique_lock<std::mutex> lock(mtx_);
    cv_.wait(lock, [this]() {
        return data_requested_ || shutdown_;
    });
    
    if (data_requested_) {
        data_requested_ = false; // 消费请求
    }
}

bool AudioManager::checkRequest() {
    std::lock_guard<std::mutex> lock(mtx_);
    return data_requested_;
}

void AudioManager::shutdown() {
    {
        std::lock_guard<std::mutex> lock(mtx_);
        shutdown_ = true;
        std::cout << "AudioManager: 关闭信号已设置" << std::endl;
    }
    cv_.notify_all();
}

bool AudioManager::isShutdown() const {
    return shutdown_;
}

request_receiver2.h

cpp
#ifndef REQUEST_RECEIVER2_H
#define REQUEST_RECEIVER2_H

#include <thread>
#include <atomic>

class RequestReceiver2 {
private:
    std::thread receiver_thread_;
    std::atomic<bool> running_;

public:
    RequestReceiver2();
    ~RequestReceiver2();
    
    void start();
    void stop();
    
private:
    void runReceiver();
};

#endif

request_receiver2.cpp

cpp
#include "request_receiver2.h"
#include "audio_manager.h"
#include <iostream>
#include <chrono>

RequestReceiver2::RequestReceiver2() : running_(false) {}

RequestReceiver2::~RequestReceiver2() {
    stop();
}

void RequestReceiver2::start() {
    if (!running_) {
        running_ = true;
        receiver_thread_ = std::thread(&RequestReceiver2::runReceiver, this);
        std::cout << "RequestReceiver2: 启动" << std::endl;
    }
}

void RequestReceiver2::stop() {
    if (running_) {
        running_ = false;
        if (receiver_thread_.joinable()) {
            receiver_thread_.join();
        }
        std::cout << "RequestReceiver2: 停止" << std::endl;
    }
}

void RequestReceiver2::runReceiver() {
    auto& audio_manager = AudioManager::getInstance();
    
    while (running_ && !audio_manager.isShutdown()) {
        // 模拟接收外部请求
        std::this_thread::sleep_for(std::chrono::seconds(2));
        
        if (!running_) break;
        
        // 通知管理器有数据请求
        audio_manager.signalDataRequest();
        std::cout << "[" << std::this_thread::get_id() 
                  << "] RequestReceiver2: 已发送数据请求信号" << std::endl;
    }
}

audio_sender2.h

cpp
#ifndef AUDIO_SENDER2_H
#define AUDIO_SENDER2_H

#include <thread>
#include <atomic>

class AudioSender2 {
private:
    std::thread sender_thread_;
    std::atomic<bool> running_;

public:
    AudioSender2();
    ~AudioSender2();
    
    void start();
    void stop();
    
private:
    void runSender();
    void processAudioData();
};

#endif

audio_sender2.cpp

cpp
#include "audio_sender2.h"
#include "audio_manager.h"
#include <iostream>
#include <chrono>
#include <vector>

AudioSender2::AudioSender2() : running_(false) {}

AudioSender2::~AudioSender2() {
    stop();
}

void AudioSender2::start() {
    if (!running_) {
        running_ = true;
        sender_thread_ = std::thread(&AudioSender2::runSender, this);
        std::cout << "AudioSender2: 启动" << std::endl;
    }
}

void AudioSender2::stop() {
    if (running_) {
        running_ = false;
        if (sender_thread_.joinable()) {
            sender_thread_.join();
        }
        std::cout << "AudioSender2: 停止" << std::endl;
    }
}

void AudioSender2::runSender() {
    auto& audio_manager = AudioManager::getInstance();
    
    while (running_ && !audio_manager.isShutdown()) {
        // 等待数据请求
        audio_manager.waitForRequest();
        
        if (!running_ || audio_manager.isShutdown()) break;
        
        // 处理音频数据
        processAudioData();
    }
}

void AudioSender2::processAudioData() {
    std::cout << "[" << std::this_thread::get_id() 
              << "] AudioSender2: 开始处理音频数据" << std::endl;
    
    // 模拟音频数据处理和发送
    for (int i = 0; i < 3; ++i) {
        std::cout << "[" << std::this_thread::get_id() 
                  << "] 处理音频数据块 " << i + 1 << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
    }
    
    std::cout << "[" << std::this_thread::get_id() 
              << "] AudioSender2: 音频数据处理完成" << std::endl;
}

main.cpp

cpp
#include <iostream>
#include <chrono>
#include <signal.h>

// 方案1的头文件
#include "request_receiver.h"
#include "audio_sender.h"

// 方案2的头文件  
#include "request_receiver2.h"
#include "audio_sender2.h"
#include "audio_manager.h"

// 全局标志,用于优雅退出
std::atomic<bool> g_shutdown(false);

void signalHandler(int signal) {
    std::cout << "\n收到信号 " << signal << ", 正在关闭..." << std::endl;
    g_shutdown = true;
}

void testScheme1() {
    std::cout << "=== 测试方案1: 全局变量方式 ===" << std::endl;
    
    RequestReceiver receiver;
    AudioSender sender;
    
    receiver.start();
    sender.start();
    
    // 运行10秒
    std::this_thread::sleep_for(std::chrono::seconds(10));
    
    receiver.stop();
    sender.stop();
}

void testScheme2() {
    std::cout << "\n=== 测试方案2: 管理器单例方式 ===" << std::endl;
    
    RequestReceiver2 receiver;
    AudioSender2 sender;
    
    receiver.start();
    sender.start();
    
    // 运行10秒
    std::this_thread::sleep_for(std::chrono::seconds(10));
    
    // 优雅关闭
    AudioManager::getInstance().shutdown();
    receiver.stop();
    sender.stop();
}

int main() {
    // 设置信号处理
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);
    
    // 测试两种方案
    testScheme1();
    testScheme2();
    
    std::cout << "程序正常退出" << std::endl;
    return 0;
}

编译命令

bash
# 编译所有文件
g++ -std=c++11 -pthread -I. \
    shared_resources.cpp \
    request_receiver.cpp audio_sender.cpp \
    audio_manager.cpp request_receiver2.cpp audio_sender2.cpp \
    main.cpp -o audio_system

# 或者分别编译
g++ -std=c++11 -pthread -c shared_resources.cpp -o shared_resources.o
g++ -std=c++11 -pthread -c request_receiver.cpp -o request_receiver.o
# ... 其他文件类似
g++ -std=c++11 -pthread *.o -o audio_system

方案比较

 
 
方案 优点 缺点
全局变量 简单直接 全局状态,耦合度高
管理器单例 封装性好,易于维护 需要额外的管理器类

推荐使用方案2(管理器单例),因为它提供了更好的封装性和可维护性,符合面向对象的设计原则。

 

 

扩展:单例模式的深入理解

如何使用单例对象执行动作

cpp
// main.cpp
int main() {
    // 方式1:直接链式调用(最常见)
    AudioManager::getInstance().playSound("explosion.wav");
    AudioManager::getInstance().setVolume(80);

    // 方式2:通过引用变量(你的例子中的方式)
    auto& audio_manager = AudioManager::getInstance();
    audio_manager.playSound("background_music.mp3");
    audio_manager.setVolume(50);

    // 方式3:通过指针(不推荐,但可行)
    AudioManager* audio_ptr = &AudioManager::getInstance();
    audio_ptr->playSound("gunshot.wav");

    return 0;
}

执行过程详解

让我们一步步分析 AudioManager::getInstance().playSound("explosion.wav") 的执行:

  1. AudioManager::getInstance() 被调用

  2. 在 getInstance() 方法内部:

    • 如果是第一次调用:创建 AudioManager 对象(调用私有构造函数)

    • 如果是后续调用:直接返回已存在的对象引用

  3. .playSound("explosion.wav") 在这个返回的对象引用上调用成员函数

实际内存模型

text
内存布局:
┌──────────────────┐
│ 静态存储区        │
│                  │
│  AudioManager    │ ← 唯一的实例存放在静态区域
│   instance       │
│                  │
└──────────────────┘
         ↑
         │
getInstance() 返回这个对象的引用
posted @ 2025-11-18 10:34  FBshark  阅读(3)  评论(0)    收藏  举报