使用互斥元进行线程安全的延迟初始化
像下面这段代码一样的朴素的转换,会引起使用该资源的线程产生不必要的序列化。这是因为每个线程都必须等待互斥元,以检查资源是否已经被初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13
| std::shared_ptr<Resource> resource_ptr; std::mutex m;
void foo() { std::unique_lock<std::mutex> lk(m); if (!source_ptr) { resource_ptr.reset(new Resource); } lk.unlock(); resource_ptr->do_something(); }
|
语气锁定互斥元并且显式地检查指针,还不如每个线程都可以用 std::call_once
,到 call_once
返回时,指针将会被某个线程初始化(以完全同步的方式),这样就安全了。使用 std::call_once
比显式地使用互斥元通常会有更低的开销,特别是初始化已经完成的时候。
1 2 3 4 5 6 7 8 9 10 11 12 13
| std::sharef_ptr<Resource> resource_ptr; std::once_flag resource_flag;
void init_resource() { resource_ptr.reset(new Resource); }
void foo() { std::call_once(resource_flag, init_resource); resource_ptr->do_something(); }
|
std::call_once()
可以容易地用于类成员的延迟出初始化:
1 2 3 4 5
| void X::call() { std::call_once(init_flag, &X:init, this); do_something(); }
|