使用带有 std::condition_variable_any 的虚拟假锁是否安全?
Is it safe to use a dummy fake lock with std::condition_variable_any?
我正在寻找的是 linux 内核中的 wait_queue_t 之类的东西。我与同步相关的底层数据结构是无锁的,因此不需要互斥锁的保护。
必须获取 std::mutex
才能使用 std::condition_variable
来阻塞等待似乎引入了不必要的开销。
我知道 linux 有 futex
,Windows 有 WaitOnAddress
,但我对这里的语言标准更感兴趣。
根据 cppreference wiki std::condition_variable_any
可以使用任何自定义 Lockable
。
那么,如果我将它与如下所示的虚拟假锁一起使用会怎样:
class FakeLock
{
public:
inline void lock() {}
inline void unlock() {}
inline bool try_lock() { return true; }
};
template<typename WaitLock=FakeLock>
class WaitQueue
{
private:
WaitLock m_lock;
std::condition_variable_any m_cond;
public:
inline void wait()
{
std::unique_lock<WaitLock> lock(m_lock);
m_cond.wait(lock);
}
inline void notify_one()
{
m_cond.notify_one();
}
inline void notify_all()
{
m_cond.notify_all();
}
};
以使用 linux 内核 wait_queue_t
的方式使用上述 WaitQueue
是否存在意外行为的潜在风险?
再次思考这个问题我想我找到了答案。
给定一个 class LockFreeStack
这是一个无锁堆栈。
考虑以下代码:
WaitQueue wq;
LockFreeStack<std::string> stack;
std::thread t1([&]() {
while (true) {
// sleep for some time
stack.push(/* random string */); // (1.1)
wq.notify_one(); // (1.2)
}
});
std::thread t2([&]() {
while (true) {
if (stack.empty()) // (2.1)
wq.wait(); // (2.2)
auto str = stack.pop();
// print string
}
});
在没有用真正的锁保护wait_queue/条件变量的情况下,两个线程有可能按如下顺序执行:
(2.1)
(1.1)
(1.2)
(2.2)
制作话题 t2
完全错过了 t1
的最新更新。 t2
将只能在下一次 notify_one()
调用后恢复执行。
真正的锁是必要的,以保证条件变量随着实际 data/state 兴趣自动改变。
我正在寻找的是 linux 内核中的 wait_queue_t 之类的东西。我与同步相关的底层数据结构是无锁的,因此不需要互斥锁的保护。
必须获取 std::mutex
才能使用 std::condition_variable
来阻塞等待似乎引入了不必要的开销。
我知道 linux 有 futex
,Windows 有 WaitOnAddress
,但我对这里的语言标准更感兴趣。
根据 cppreference wiki std::condition_variable_any
可以使用任何自定义 Lockable
。
那么,如果我将它与如下所示的虚拟假锁一起使用会怎样:
class FakeLock
{
public:
inline void lock() {}
inline void unlock() {}
inline bool try_lock() { return true; }
};
template<typename WaitLock=FakeLock>
class WaitQueue
{
private:
WaitLock m_lock;
std::condition_variable_any m_cond;
public:
inline void wait()
{
std::unique_lock<WaitLock> lock(m_lock);
m_cond.wait(lock);
}
inline void notify_one()
{
m_cond.notify_one();
}
inline void notify_all()
{
m_cond.notify_all();
}
};
以使用 linux 内核 wait_queue_t
的方式使用上述 WaitQueue
是否存在意外行为的潜在风险?
再次思考这个问题我想我找到了答案。
给定一个 class LockFreeStack
这是一个无锁堆栈。
考虑以下代码:
WaitQueue wq;
LockFreeStack<std::string> stack;
std::thread t1([&]() {
while (true) {
// sleep for some time
stack.push(/* random string */); // (1.1)
wq.notify_one(); // (1.2)
}
});
std::thread t2([&]() {
while (true) {
if (stack.empty()) // (2.1)
wq.wait(); // (2.2)
auto str = stack.pop();
// print string
}
});
在没有用真正的锁保护wait_queue/条件变量的情况下,两个线程有可能按如下顺序执行:
(2.1)
(1.1)
(1.2)
(2.2)
制作话题 t2
完全错过了 t1
的最新更新。 t2
将只能在下一次 notify_one()
调用后恢复执行。
真正的锁是必要的,以保证条件变量随着实际 data/state 兴趣自动改变。