简短的回答是,您的代码可能正确或错误;你没有确切地展示如何full
被操纵。
C++ 代码的个别位永远不是线程安全的。线程安全是代码的一种关系属性;如果它们永远不会导致竞争条件,那么两位代码相对于彼此可以是线程安全的。
但是一点代码永远不是线程安全的;说某事是线程安全的就像说某事是“相同的高度”。
“monkey see monkey do”条件变量模式是这样的:
template<class T>
class cv_bundle {
std::mutex m;
T payload;
std::condition_variable cv;
public:
explicit cv_bundle( T in ):payload(std::move(in)) {}
template<class Test, class Extract>
auto wait( Test&& test, Extract&& extract ) {
std::unique_lock<std::mutex> l(m);
cv.wait( l, [&]{ return test(payload); } );
return extract(payload);
}
template<class Setter>
void load( Setter&& setter, bool only_one = true ) {
std::unique_lock<std::mutex> l(m);
bool is_set = setter( payload );
if (!is_set) return; // nothing to notify
if (only_one)
cv.notify_one();
else
cv.notify_all();
}
};
test
T& payload
如果有东西要消耗(即唤醒不是虚假的),则接受 a并返回 true。
extract
接受 aT& payload
并返回你想要的任何信息。它通常应该重置有效载荷。
setter
以将返回T& payload
的方式修改. 如果这样做,它会返回. 如果它选择不这样做,它会返回。test
true
true
false
所有 3 个都在对T payload
.
现在,您可以对此产生变化,但这样做很难做到正确。例如,不要假设原子有效负载意味着您不必锁定互斥锁。
虽然我将这 3 个东西捆绑在一起,但您可以将单个互斥锁用于一堆条件变量,或者将互斥锁用于多个条件变量。有效载荷可以是一个布尔值、一个计数器、一个数据向量,或者更陌生的东西;一般来说,它必须始终受到互斥体的保护。如果它是原子的,则在被修改的值和通知之间的开放间隔中的某个时间点,必须锁定互斥锁,否则您可能会丢失通知。
手动循环控制而不是传入 lambda 是一种修改,但是描述哪种手动循环是合法的以及哪些是竞争条件是一个复杂的问题。
实际上,除非我有充分的理由,否则我避免离开这种猴子见猴做“货物崇拜”的条件变量使用方式。然后我被迫阅读 C++ 内存和线程模型,这让我很不开心,这意味着我的代码很可能不正确。
请注意,如果传入的任何 lambda 并回调到cv_bundle
我显示的代码中不再有效。