保护共享数据的替代设施
一. std::once_flag 和 std::call_once
std::shared_ptr resource_ptr;
std::once_flag resource_flag; // 1
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag,init_resource); // 可以完整的进行一
次初始化
resource_ptr->do_something();
}
二. 一个 std::call_once 的替代方案
class my_class;
my_class& get_my_class_instance()
{
static my_class instance; // 线程安全的初始化过程
return instance;
}
多线程可以安全的调用get_my_class_instance()①函数,不用为数据竞争而担心。
三. 使用 boost::shared_mutex 对数据结构进行保护
用 std::lock_guard 和 std::unique_lock 上锁。作为std::mutex 的替代方案,与 std::mutex 所做的一样,这就能保证更新线程的独占访问。因为其他线程不需要去修改数据结构,所以其可以使用 boost::shared_lock 获取访问权。这与使用 std::unique_lock 一样,除非多线程要在同时获取同一个 boost::shared_mutex 上有共享锁。唯一的限制:当任一线程拥有一个共享锁时,这个线程就会尝试获取一个独占锁,直到其他线程放弃他们的锁;同样的,当任一线程拥有一个独占锁时,其他线程就无法获得共享锁或独占锁,直到第一个线程放弃其拥有的锁。
使用 std::map 持有缓存数据,使用 boost::shared_mutex 进行保护。
#include
find_entry()使用 boost::shared_lock<> 来保护共享和只读权限①;这就使得多线程可以同时调用find_entry(),且不会出错。另一方面,update_or_add_entry()使用 std::lock_guard<> 实例,当表格需要更新时②,为其提供独占访问权限;update_or_add_entry()函数调用时,独占锁会阻止其他线程对数据结构进行修改,并且阻止线程调用find_entry()。
四. 嵌套锁
std::lock_guard 和 std::unique_lock 嵌套锁一般用在可并发访问的类上,所以其拥互斥量保护其成员数据。每个公共成员函数都会对互斥量上锁,然后完成对应的功能,之后再解锁互斥量。不过,有时成员函数会调用另一个成员函数,这种情况下,第二个成员函数也会试图锁住互斥量,这就会导致未定义行为的发生。“变通的”解决方案会将互斥量转为嵌套锁,第二个成员函数就能成功的进行上锁,并且函数能继续执行。但是,这样的使用方式是不推荐的,因为其过于草率,并且不合理。