std::call_once && std::once_flag notes
多线程保护数据时,一种较为特殊的情况是只需要保护资源的初始化。
资源初始化一般遵循"lazy initialization"的原则,也就是在用到该资源最近的地方再初始化。
比较容易想到的办法是用std::mutex,将资源初始化的地方锁起来,如下:
1 std::shared_ptr<some_resource> resource_ptr;
2 std::mutex resource_mutex;
3 void foo()
4 {
5 std::unique_lock<std::mutex> lk(resource_mutex);
6 if(!resource_ptr)
7 {
8 resource_ptr.reset(new some_resource);
9 }
10 lk.unlock();
11 resource_ptr->do_something();
12 }
13
这确实是一个办法。但是初始化时如果需要耗费比较多的时间,当有比较多的线程时,一个线程初始化时,其他线程会耗时间在不必要的等待上。
在c++11以后,我们可以使用std::once_flag和std::call_once来解决资源初始化时加锁的问题。比起显示调用std::mutex的好处是,资源消耗更少。
下面是两个例子:
1 std::shared_ptr<some_resource> resource_ptr;
2 std::once_flag resource_flag;
3 b
4 void init_resource()
5 {
6 resource_ptr.reset(new some_resource);
7 }
8 void foo()
9 {
10 std::call_once(resource_flag,init_resource);
11 resource_ptr->do_something();
12 }
13
14
15
16
17 class X
18 {
19 private:
20 connection_info connection_details;
21 connection_handle connection;
22 std::once_flag connection_init_flag;
23 void open_connection()
24 {
25 connection=connection_manager.open(connection_details);
26 }
27 public:62
28 C HAPTER 3 Sharing data between threads
29 X(connection_info const& connection_details_):
30 connection_details(connection_details_)
31 {}
32 void send_data(data_packet const& data)
33 {
34 std::call_once(connection_init_flag,&X::open_connection,this);
35 connection.send_data(data);
36 }
37 data_packet receive_data()
38 {
39 std::call_once(connection_init_flag,&X::open_connection,this);
40 return connection.receive_data();
41 }
42 b
43 d
44 c
45 };
46
或者更一般地,可以解决一类在多线程环境下保证某段代码只执行一次的问题。
比如声明的一个static 变量。
参考资料: