我知道使用原子是危险的(几天前我看过Herb Sutter的3小时讲座),但是下面的用例对我来说似乎是合理的,简单而且包含得很好。
问题:(a)这有什么问题吗? (当然,必须有。)(b)这种基于混合原子/互斥的方法是否有名称? (c)是否有更简单的方法来实现同样的目标?
我们的目标是拥有一个线程安全的计数器类,我们可以调用attempt_invalidation()
,知道如果计数为零,它只会将invalid
标志设置为true
。课堂上没有其他公共方法,但我们会有一个专门设计用于RAII递增/递减计数器的朋友类。
class hybrid_counter{
friend class hybrid_counter_user;
bool invalidated = false;
int counter_a = 0;
std::atomic_int counter_b;
std::mutex mu;
bool increment_safely(){
std::lock_guard<std::mutex> gaurd(mu);
if ( !invalidated )
counter_a++;
return invalidated;
};
void increment_dangerously(){
counter_b++;
};
void decrement(){
counter_b--;
};
public:
bool attempt_invalidation(){
if(counter_a + counter_b == 0){
std::lock_guard<std::mutex> gaurd(mu);
if(counter_a + counter_b == 0)
invalidated = true;
}
return invalidated;
};
};
这是知道如何正确使用计数器的朋友类:
class hybrid_counter_user{
public:
hybrid_counter_user(hybrid_counter& hc){
if(hc.increment_safely() == false) // is not yet invalidated
c = &hc;
else
c = nullptr;
};
~hybrid_counter_user(){
if(c)
c->decrement();
};
hybrid_counter_user(hybrid_counter_user&& old){
c = old.c;
old.c = nullptr;
}
hybrid_counter_user(hybrid_counter_user& other){
c = other.c;
if(c)
c->increment_dangerously();
}
private:
hybrid_counter* c;
};
请注意,复制构造函数使用hybrid_counter
在other
在范围内时仍然有效的事实,other
的析构函数不能与increment_dangerously
重新排序,因为两者都涉及相同的原子变量。
移动构造函数只是转移递减的责任。