12.4 只初始化一次共享资源
问题
有一序列的线程,如何使得它们能使用那些只能初始化一次的资源?
解决方案
在线程使用这个资源前就初始化这个资源,或者如果你不能这样做的话,就调用定义在<boost/thread/once.hpp>中的call_once函数。示例12-5说明该如何使用这个call_once函数。
示例12-5 只初始化一次某个资源
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/once.hpp>
// Some sort of connection class that should only be initialized once
struct Conn {
static void init() {++i_;}
static boost::once_flag init_;
static int i_;
// ...
};
int Conn::i_ = 0;
boost::once_flag Conn::init_ = BOOST_ONCE_INIT;
void worker() {
boost::call_once(Conn::init, Conn::init_);
// Do the real work...
}
Conn c; // You probably don't want to use a global, so see the
// next Recipe
int main() {
boost::thread_group grp;
for (int i = 0; i < 100; ++i)
grp.create_thread(worker);
grp.join_all();
std::cout << c.i_ << '\n'; // c.i_ = 1
}
讨论
一个共享资源不得不再在某个地方被初始化,并且你可能期望第一次使用这个资源的线程来完成它的初始化工作。一个once_flag类型(它的准确类型由平台本身决定)和这个call_once函数能够保证多个线程不会重复地初始化同一个对象。但是你不得不做两件事。
首先,使用BOOST_ONCE_INIT宏来初始化这个once_flag类型的对象。这个宏的具体值由平台本身决定。示例12-5中,类Conn代表某种类型的连接(数据库、网络套接字、硬件等),我期望这个连接只初始化一次,尽管多个线程可能都会初始化它。常常当你想动态地装载一个库时这种情况就会出现,这个库可能是在一个应用程序的配置文件中指明的。这个once_flag是一个静态类变量,因为我仅期望初始化一次,而不管这个类可能有多个实例。因此,我给这个变量赋一个BOOST_ONCE_INIT值作为起始值,如下所示:
boost::once_flag Conn::initFlag_ = BOOST_ONCE_INIT;
然后,在我的工作函数中,我调用call_once函数,它同步对这个initFlag_变量的访问,这样就禁止了对这个变量的并发访问。我给这个函数传递了两个参数:
boost::call_once(Conn::init, Conn::initFlag_);
第一个参数用来做初始化函数的地址。第二个参数就是这个标志。这样的话,多个线程都可以初始化这个Conn类,但是只有一个会成功。






