gpt4 book ai didi

C++ 像从另一个线程一样锁定互斥量?

转载 作者:行者123 更新时间:2023-11-30 00:43:56 26 4
gpt4 key购买 nike

我正在编写一个 Audio 类,它包含一个 std::thread 用于异步填充一些缓冲区。假设我们调用主线程 A 和后台(类成员)线程 B。我正在使用 std::mutex 在声音未播放时阻止线程 B,那样它就不会不需要时在后台运行,不会占用过多的 CPU 资源。默认情况下,线程 A 锁定互斥量,因此线程 B 被阻塞,然后在播放声音时线程 A 解锁互斥量,线程 B 循环运行(通过锁定然后立即解锁)。

当线程 B 发现它已到达文件末尾时,问题就出现了。它可以停止播放和清理缓冲区等,但它不能停止自己的循环,因为线程 B 无法锁定线程 A 的互斥量。

相关代码大纲如下:

class Audio {
private:

// ...

std::thread Thread;
std::mutex PauseMutex; // mutex that blocks Thread, locked in constructor
void ThreadFunc(); // assigned to Thread in constructor

public:

// ...

void Play();
void Stop();
}

_

void Audio::ThreadFunc() {

// ... (include initial check of mutex here)

while (!this->EndThread) { // Thread-safe flag, only set when Audio is destructed

// ... Check and refill buffers as necessary, etc ...

if (EOF)
Stop();

// Attempt a lock, blocks thread if sound/music is not playing
this->PauseMutex.lock();
this->PauseMutex.unlock();
}
}

void Audio::Play() {
// ...
PauseMutex.unlock(); // unlock mutex so loop in ThreadFunc can start
}

void Audio::Stop() {
// ...
PauseMutex.lock(); // locks mutex to stop loop in ThreadFunc
// ^^ This is the issue here
}

在上面的设置中,当后台线程发现它到达 EOF 时,它会调用类的 Stop() 函数,该函数应该锁定互斥锁以停止后台线程。这是行不通的,因为互斥量必须由主线程锁定,而不是后台线程(在这个例子中,它在 ThreadFunc 中崩溃,因为后台线程在之后尝试在其主循环中锁定已经锁定在 Stop() 中。

在这一点上,我唯一能想到的就是以某种方式让后台线程锁定互斥量就好像它是主线程一样,让主线程拥有互斥量...如果那可能的话?有没有办法让线程将互斥体的所有权转移给另一个线程?或者这是我创建的设置中的设计缺陷? (如果是后者,是否有任何合理的解决方法?)到目前为止,类里面的其他所有内容都按设计工作。

最佳答案

我什至不会假装理解你的代码是如何尝试做它正在做的事情的。然而,有一件事是显而易见的。您正在尝试使用互斥锁来传达某些谓词状态更改,这是在该高速公路上行驶的错误车辆。

谓词状态变化是通过耦合三件事来处理的:

  • 一些谓词数据
  • 保护谓词的互斥体
  • 用于传达谓词状态可能变化的条件变量。

目标

以下示例的目标是演示在控制跨多个线程的程序流时如何协同使用互斥体、条件变量和谓词数据。它显示了使用 waitwait_for 条件变量功能的示例,以及一种将成员函数作为线程过程运行的方法。


下面是一个简单的 Player 类,它在四种可能的状态之间切换:

  • Stopped : 播放器没有播放,没有暂停,也没有退出。
  • Playing : 播放器正在播放
  • Paused:播放器已暂停,一旦恢复播放,将从中断处继续播放。
  • 退出:播放器应该停止正在做的事情并终止。

谓词数据相当明显。 状态 成员。它必须受到保护,这意味着除非在互斥锁的保护下,否则不能更改也不能检查。我在其中添加了一个 counter,它会在保持 Playing 状态一段时间的过程中简单地递增。更具体地说:

  • 播放时,计数器 每 200 毫秒递增一次,然后将一些数据转储到控制台。
  • 暂停时,计数器 不会改变,但会保留播放时的最后一个值。这意味着当恢复时,它将从中断的地方继续。
  • 停止时,计数器 重置为零,并在控制台输出中注入(inject)换行符。这意味着切换回播放将重新开始计数器序列。
  • 设置 Quit 状态对 counter 没有影响,它将与其他所有内容一起消失。

代码

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unistd.h>

using namespace std::chrono_literals;

struct Player
{
private:
std::mutex mtx;
std::condition_variable cv;
std::thread thr;

enum State
{
Stopped,
Paused,
Playing,
Quit
};

State state;
int counter;

void signal_state(State st)
{
std::unique_lock<std::mutex> lock(mtx);
if (st != state)
{
state = st;
cv.notify_one();
}
}

// main player monitor
void monitor()
{
std::unique_lock<std::mutex> lock(mtx);
bool bQuit = false;

while (!bQuit)
{
switch (state)
{
case Playing:
std::cout << ++counter << '.';
cv.wait_for(lock, 200ms, [this](){ return state != Playing; });
break;

case Stopped:
cv.wait(lock, [this]() { return state != Stopped; });
std::cout << '\n';
counter = 0;
break;

case Paused:
cv.wait(lock, [this]() { return state != Paused; });
break;

case Quit:
bQuit = true;
break;
}
}
}

public:
Player()
: state(Stopped)
, counter(0)
{
thr = std::thread(std::bind(&Player::monitor, this));
}

~Player()
{
quit();
thr.join();
}

void stop() { signal_state(Stopped); }
void play() { signal_state(Playing); }
void pause() { signal_state(Paused); }
void quit() { signal_state(Quit); }
};

int main()
{
Player player;
player.play();
sleep(3);
player.pause();
sleep(3);
player.play();
sleep(3);
player.stop();
sleep(3);
player.play();
sleep(3);
}

输出

我无法真正证明这一点。您必须运行它并查看它是如何工作的,我邀请您像我上面那样玩弄 main() 中的状态。但是请注意,一旦 quit 被调用,则不会监视任何其他状态。设置退出状态将关闭监视器线程。对于它的值(value),上面的运行应该看起来像这样:

1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

第一组数字被分成两组(1..15,然后是 16..30),作为播放的结果,然后暂停,然后再次播放。然后发出停止信号,然后再播放约 3 秒。之后,对象自毁,并在这样做时设置 Quit 状态,并等待监视器终止。

总结

希望你能从中有所收获。如果您发现自己试图通过手动锁定和释放互斥锁来管理谓词状态,那么您需要一个条件变量设计模式来帮助检测这些变化。

希望你能从中有所收获。

关于C++ 像从另一个线程一样锁定互斥量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51668477/

26 4 0