大多数函数都不是可重入的。这也就是说在某一个线程已经调用了一个函数时,如果你再调用同一个函数,那么这样是不安全的。举例来说,std::strtok就是不可重入的,因为它使用静态变量来保存要被分割成符号的字符串。
一个不可重入的函数通过连续的调用来保存静态变量或者是返回一个指向静态数据的指针,有两种方法可以让不可重用的函数变成可重用的函数。
方法1:就是改变接口,用指针或引用代替原先使用静态数据的地方。比方说,POSIX定义了strok_r,std::strtok中的一个可重入的变量,它用一个额外的char**参数来代替静态数据。这种方法很简单,而且提供了可能的最佳效果。但是这样必须改变公共接口,也就意味着必须改代码。
方法2:不用改变公有接口,而是用本地存储线程(Thread-Locally Storage)来代替静态数据(有时也被成为特殊线程存储,thread-specific storage)。
Boost线程库提供了智能指针boost::thread_specific_ptr来访问本地存储线程。thread_specific_ptr线程局部存储的包装,它可用于封装线程独立的全局变量。每一个线程第一次使用这个智能指针的实例时,它的初值是NULL(所以必须要先检查这个它的值是否为空),在每个线程使用它之前需要new一个对象交给全局的threah_specific_ptr进行管理,当线程退出后,Boost线程库保证本地存储线程中保存的数据会在线程结束后被清除,这样,各个线程就可以各自独立地访问这个全局变量的本地存储版本,线程之间就不会因为访问同一全局对象而引起资源竞争导致性能下降。而线程结束时,这个资源会被自动释放。
它可以应用在以下两种场景:
1. 改编一个原本设计用于单线程的库接口,比如libc里的strtok函数。这种库一般隐含的使用一个全局变量,可以使用thread_specific_ptr控制全局变量,使其可用于多线程。
2. 线程中使用了一系列的方法/函数,它们需要一个逻辑上的全局变量来共享数据,但实际上这个变量是线程独立的。
thread_specific_ptr代表了某个全局变量的本地存储,各个线程可以各自独立地通过它访问这个全局变量的本地副本,起到了井水不犯河水的效果。
使用boost::thread_specific_ptr的简单例子。
#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;
}
其中创建了两个线程来初始化本地存储线程,并有10次循环,每一次都会增加智能指针指向的值,并将其输出到std::cout上(由于std::cout是一个共享资源,所以通过互斥体进行同步)。main线程等待这两个线程结束后就退出。从这个例子输出可以明白的看出每个线程都处理属于自己的数据实例,尽管它们都是使用同一个boost::thread_specific_ptr。
参考:
https://blog.csdn.net/liujiayu2/article/details/50587084
https://blog.csdn.net/flyingleo1981/article/details/47083737
来源:CSDN
作者:Glücklichste
链接:https://blog.csdn.net/sunlin972913894/article/details/103647149