resource> resource_ptr;
std::once_flag resource_flag;
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag, init_resource);
resource_ptr->do_something();
}
C++11规定了局部静态变量的初始化只会在某个单一线程上发生,在初始化完成之前,其它线程不会越过静态数据的声明而继续运行。如果某些类只需要用到唯一一个全局实例,这种情况下可以用以下方法代替std::call_once
:
class my_class;
my_class& get_my_class_instance()
{
static my_class instance;
return instance;
}
3.2、保护不常更新的数据
如果我们想要允许单独一个“写线程”进行完全排他的访问,也允许多个“读线程”共享数据或并发访问,那么可以使用C++17提供的新互斥量std::shared_mutex
。对于更新操作,使用std::lock_guard<std::shared_mutex>
或std::unique_lock<std::shared_mutex>
锁定,代替对应的std::mutex
特化,它们都保证了访问的排他性质。对于无需更新数据结构的线程,可以另行改用共享锁std::shared_lock<std::shared_mutex>
,多个线程能够同时锁住同一个std::shared_mutex
。
class dns_entry {};
class dns_cache
{
std::map<std::string, dns_entry> entries;
std::shared_mutex entry_mutex;
public:
dns_entry find_entry(const std::string& domain)
{
std::shared_lock<std::shared_mutex> lk(entry_mutex);
auto it = entries.find(domain);
return it == entries.end() ? dns_entry() : it->second;
}
void update_or_add_entry(const std::string& domain, const dns_entry& dns_details)
{
std::lock_guard<std::shared_mutex> lk(entry_mutex);
entries[domain] = dns_details;
}
};
3.3、递归加锁
标准库提供了std::recursive_mutex
,其工作方式与std::mutex
相似,不同之处是其允许同一线程对某互斥量的同一实例多次加锁。假如我们对它调用3次lock()
,就必须调用3次unlock()
才能解锁。