【CI130x 离在线】音频下发时条件变量的使用
背景
基于Linux C++,有这么两个线程,一个线程收到从机请求数据了的信号,另一个线程等待这个信号,并将音频下发。
假如是两个类(这两个线程分属于两个不同的cpp/h 文件),那么条件变量如何使用?
正文
当条件变量需要在两个不同的类(分属不同的cpp/h文件)中使用时,需要仔细设计接口和共享状态的管理。我来提供几种解决方案:
方案1:使用全局条件变量和互斥量
shared_resources.h
#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
#include "shared_resources.h"
// 定义全局的同步原语
std::mutex g_audio_mutex;
std::condition_variable g_audio_cv;
bool g_data_requested = false;
request_receiver.h
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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;
}
编译命令
# 编译所有文件
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(管理器单例),因为它提供了更好的封装性和可维护性,符合面向对象的设计原则。
扩展:单例模式的深入理解
如何使用单例对象执行动作
// 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") 的执行:
-
AudioManager::getInstance()被调用 -
在
getInstance()方法内部:-
如果是第一次调用:创建
AudioManager对象(调用私有构造函数) -
如果是后续调用:直接返回已存在的对象引用
-
-
.playSound("explosion.wav")在这个返回的对象引用上调用成员函数
实际内存模型
内存布局:
┌──────────────────┐
│ 静态存储区 │
│ │
│ AudioManager │ ← 唯一的实例存放在静态区域
│ instance │
│ │
└──────────────────┘
↑
│
getInstance() 返回这个对象的引用

浙公网安备 33010602011771号