我需要让实时(音频)线程在事件发生时向另一个后台线程发出信号。后台“检查器”线程将执行一些昂贵的操作。
因此我的限制是:
通知操作必须是无锁的(等待端无此限制)
防止检查器线程浪费 CPU 时间,并在不需要时将其适当休眠,因为事件之间可能相隔几分钟。
- 代码必须是可移植的(osx、windows、android、ios)。
我想出了一个使用条件变量和条件标志的简单解决方案。当条件变量在检查器线程开始等待之前发出信号时,需要该标志来防止检查器线程等待(可能永远)。
struct EventChecker {
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> flag;
bool alive;
std::thread checkerThread;
EventChecker() {
alive = true;
checkerThread = std::thread([this]() {
while (alive) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return flag.exchange(false); });
std::cout << " action! ";
}
});
}
void notify() {
flag.store(true);
cv.notify_one();
}
~EventChecker() {
alive = false;
notify();
checkerThread.join();
}
};
如果 notify()
发生在标志检查和等待的实际开始之间,此解决方案将死锁(准确地说,检查线程未被唤醒)。如果您认为带条件的 wait
的实现只是:
while (!predicate())
wait(lock);
通过这个简单的测试,很容易出现死锁(在 macbook 上重现):
int main() {
for (int i = 0; i < 10000; i++) {
EventChecker ec;
}
return 0;
}
据我了解,如果不在 notify 中锁定 mtx
并在等待端添加解锁以减少保留时间,实际上没有办法使 check+wait 操作原子化,如下所示:
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return flag.exchange(false); });
// NB: wait() atomically releases lock and blocks the thread
mtx.unlock();
我错过了什么吗?我是否滥用了 std::condition_variable
并且还有其他更适合我的目的?
最佳答案
对于没有锁的原子check-and-wait,你可以使用futex机制。不幸的是,这种机制不是任何 C/C++ 标准的一部分,它是低级操作系统支持的一部分:
- Linux(还有 Android)有 futex系统调用。
- Windows 有 WaitOnAddress功能。
我还没有在 OSX 上找到任何 futex 的类似物,并且不知道它在 ios 上。
关于c++ - 具有条件和无锁通知的可移植事件检查器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49834624/