很多程序实现的时候并没有设计成可重入。这种函数在使用的时候一定要小心,特别是在多线程的情况下。因为当别的函数正在调
用这个函数时,你再调用这个函数,那么很可能得到的结果是unpredictive的。一般情况下如果这个函数使用了静态的数据那么
这样的程序一般是不可重入的。
例如std::strtok就是一个不可重入的函数,因为它是利用静态数据来保存被分成token的string的。
一个不可重入程序要转换成一个可重入的程序一般通过两种方法可以实现。
一种方法是修改程序的接口这就意味着要修改很多的代码。另外的一种方法是不改变接口但是改静态的存储为进程局部的存储。有
时也叫thread-specific storage。
thread 局部存储是跟特定的线程相关的。多线程库给出访问这些局部存储的接口。这样程序就可以变得可重入了。但是线程访问
thread locak的存储会比访问静态的或者局部的对象。但是这也是一种不得以的解决方法而以。这也是在不改变接口的前提下的
唯一的解决方案。
boost.thread 通过 智能指针 boost::thread_specific_ptr来访问thread local 存储。在使用的时候一定先要初始化指针。
boost.thread能保证把在 thread local storage 在thread退出线程时,自动清理掉。
下面是一个例子:
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
#include <iostream>
boost::mutex io_mutex;
boost::thread_specific_ptr<int> ptr;
struct count
{
count(int id) : id(id) { }
void operator()()
{
if (ptr.get() == 0)
ptr.reset(new int(0));
for (int i = 0; i < 10; ++i)
{
(*ptr)++;
boost::mutex::scoped_lock
lock(io_mutex);
std::cout << id << ": "
<< *ptr << std::endl;
}
}
int id;
};
int main(int argc, char* argv[])
{
boost::thread thrd1(count(1));
boost::thread thrd2(count(2));
thrd1.join();
thrd2.join();
return 0;
}
还有一个需要处理的东西的是怎样保证初始化子程序是thread-safe的。
#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;
}