使用mutex的时候要尽量缩小临界区,若可能的话,对mutex加锁仅仅是为了获取共享数据,而对数据的计算放在临界区之外。a lock should be held for only the minimum possible time needed to perform the required operations。一个非常好的实例就是copy on write。不要在临界区内进行IO,IO比内存读取慢100倍以上。在不需要mutex的时候一定要注意解锁,这通常对临界区的lock_guard单独开辟一个scope就可以实现,当然对已可以实现解锁的unique_lock可以灵活实现任意地方对其所管理的mutex的解锁。一个unique_lock灵活运用的例子如下:
void get_and_process_data()
{
std::unique_lock
my_lock(the_mutex);//加锁,获取数据
some_class data_to_process=get_next_data_chunk();
my_lock.unlock(); //解锁
result_type result=process(data_to_process);//对数据的处理
my_lock.lock(); //再加锁,将处理结构重写,从而不需要对整个get_and_process_data过程上锁
write_result(data_to_process,result);//将处理结果重写
}
下面看一个缩小临界区带来的改变:
class Y
{
private:
int some_detail;
mutable std::mutex m;
int get_detail() const
{
std::lock_guard
lock_a(m);
return some_detail;//由于是int,直接返回也不费事
}
public:
Y(int sd):some_detail(sd){}
friend bool operator==(Y const& lhs, Y const& rhs)
{
if(&lhs==&rhs)
return true;
int const lhs_value=lhs.get_detail();//单独上锁
int const rhs_value=rhs.get_detail();
return lhs_value==rhs_value;
}
};
本来在比较操作时应该对lhs和rhs同时加锁lock(lhs.m,rhs.m),为了缩小临界区加之int很小,改成在一个时刻只对一个对象加锁,这样可能出现一个问题: 比较的结果是不同时刻的两个对象,若lhs发生阻塞,而rhs随后被修改,结果并非是想要的。if you
don’t hold the required locks for the entire duration of an operation, you’re exposing yourself to race conditions。所有关于临界区的设计需要特别小心,这里只是浅谈了下而已。