我的应用程序中有一个单独的音频线程,因为它在当时听起来是个好主意,但现在我对其他线程如何与音频线程进行通信感到惋惜。
audioThread() {
while(!isCloseRequested) {
If(audio.dogSoundRequested) {
audio.playDogSound();
}
}
}
otherThread() {
Audio.dogSoundRequested();
}
这是一种有效的线程音频方式,还是您看到此设置存在问题?
答案 0 :(得分:0)
除了使用互斥锁之外,这里还有一个简单的多线程设置
// g++ -o multi_threading -pthread -std=c++11 multi_threading.cpp
#include <iostream>
#include <thread>
#include <exception>
#include <mutex>
#include <climits> // min max of short int
void launch_consumer() {
std::cout << "launch_consumer" << std::endl;
} // launch_consumer
void launch_producer(std::string chosen_file) {
std::cout << "launch_producer " << chosen_file << std::endl;
} // launch_producer
// -----------
int main(int argc, char** argv) {
std::string chosen_file = "audio_file.wav";
std::thread t1(launch_producer, chosen_file);
std::this_thread::sleep_for (std::chrono::milliseconds( 100));
std::thread t2(launch_consumer);
// -------------------------
t1.join();
t2.join();
return 0;
}
答案 1 :(得分:0)
这里的问题似乎是
1:如何使audio.dogSoundRequested
和isCloseRequested
线程安全。
2:audioThread
正忙着等待(例如无限旋转,直到audio.dogSoundRequested
变为true
。
正如其他人所建议的那样,你可以使用互斥锁来保护这两个变量,但这样做太过分了 - 另外,它通常是音频代码的好形式,不使用阻塞同步来避免priority inversion的问题。 / p>
相反,假设你正在使用C ++ 11或C ++ 14,你可以使用一个原子变量,它是轻量级的,而不是(在大多数实现中)阻塞:
#include <atomic>
...
std::atomic<bool> dogSoundRequested{false};
std::atomic<bool> isCloseRequested{false};
对std :: atomic的读取和写入与内置类型具有相同的约定,但是将生成代码以确保读取和写入相对于其他线程是原子的,并且结果与其他CPU同步
在audio.dogSoundRequested
的情况下,你想要这两种效果,在isCloseRequested
的情况下,结果立即在其他CPU上可见。
要解决忙碌等待问题,请在有事情要做时使用条件变量唤醒audioThread
:
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
audioThread()
{
while(!isCloseRequested)
{
m.lock();
cv.wait(m);
// wait() returns with the mutex still held.
m.unlock();
if(audio.dogSoundRequested)
{
audio.playDogSound();
}
}
}
void dogSoundRequested()
{
dogSoundRequested = true;
cv.notify_one();
}
答案 2 :(得分:0)
不要使用互斥锁和条件变量使代码复杂化,而应考虑创建一个线程安全的FIFO。在这种情况下,可以有多个作者和一个消费者。该应用程序的其他线程是此FIFO的编写器,audioThread()
是消费者。
// NOP = no operation
enum AudioTask {NOP, QUIT, PLAY_DOG, ...};
class Fifo
{
public:
bool can_push() const; // is it full?
void push(AudioTask t); // safely writes to the FIFO
AudioTask pop(); // safely reads from the FIFO, if empty NOP
};
现在audioThread()
更清晰一点,假设fifo
和audio
是应用程序类成员:
void audioThread()
{
bool running = true;
while(running)
{
auto task = fifo.pop();
switch(task)
{
case NOP: std::this_thread::yield(); break;
case QUIT: running = false; break;
case PLAY_DOG: audio.playDogSound(); break;
}
}
}
最后,调用代码只需要将任务推送到FIFO中:
void request_dog_sound()
{
fifo.push(PLAY_DOG);
}
void stop_audio_thread()
{
fifo.push(QUIT);
audio_thread.join();
}
这将线程安全同步的详细信息放在Fifo类中,使应用程序的其余部分保持清洁。
答案 3 :(得分:-1)
如果您想确保没有其他线程触及playDogSound()
函数,请使用互斥锁来锁定资源。
std::mutex mtx;
audioThread() {
while(!isCloseRequested) {
if (audio.dogSoundRequested) {
mtx.lock();
audio.playDogSound();
mtx.unlock();
}
}
}