您的位置:首页 > 其它

【Boost】boost库中thread多线程详解8——call_once仅运行一次

2016-01-26 14:25 543 查看
还有一个问题没有解决:如何使得初始化工作(比如说构造函数)也是线程安全的。比方说,如果一个引用程序要产生唯一的全局的对象,由于实例化顺序的问题,某个函数会被调用来返回一个静态的对象,它必须保证第一次被调用时就产生这个静态的对象。这里的问题就是如果多个线程同时调用了这个函数,那么这个静态对象的构造函数就会被调用多次,这样错误产生了。解决这个问题的方法就是所谓的“一次实现”(once routine)。“一次实现”在一个应用程序只能执行一次。如果多个线程想同时执行这个操作,那么真正执行的只有一个,而其他线程必须等这个操作结束。为了保证它只被执行一次,这个routine由另一个函数间接的调用,而这个函数传给它一个指针以及一个标志着这个routine是否已经被调用的特殊标志。这个标志是以静态的方式初始化的,这也就保证了它在编译期间就被初始化而不是运行时。因此也就没有多个线程同时将它初始化的问题了。Boost线程库提供了boost::call_once来支持“一次实现”,并且定义了一个标志boost::once_flag及一个初始化这个标志的宏BOOST_ONCE_INIT。List6是一个使用了boost::call_once的例子。其中定义了一个静态的全局整数,初始值为0;还有一个由BOOST_ONCE_INIT初始化的静态boost::once_flag实例。main函数创建了两个线程,它们都想通过传入一个函数调用boost::call_once来初始化这个全局的整数,这个函数是将它加1。main函数等待着两个线程结束,并将最后的结果输出的到std::cout。由最后的结果可以看出这个操作确实只被执行了一次,因为它的值是1。
#include <boost/thread/thread.hpp>
#include <boost/thread/once.hpp>
#include <iostream>

int i = 0;
boost::once_flag flag =
BOOST_ONCE_INIT;

void init()
{
++i;
}

void thread()
{
boost::call_once(&init, flag);
}

int main(int argc, char* argv[])
{
boost::thread thrd1(&thread);
boost::thread thrd2(&thread);
thrd1.join();
thrd2.join();
std::cout << i << std::endl;
return 0;
}


#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对象。boost::once_flag Conn::init_ = BOOST_ONCE_INIT; 之后调用call_once函数,boost::call_once(Conn::init, Conn::init_); 第一个形参是希望被执行一次的初始化函数的地址。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: