一、ThreadLocal<T>类
1、在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。
2、在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。
3、但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。
4、POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称为(Thread-specific Data,或 TSD)。
5、线程特定数据也称为线程本地存储TLS(Thread-local storage)
6、对于POD类型的线程本地存储,可以用__thread关键字

pthread_key_create、pthread_key_delete、pthread_getspecific、pthread_setspecific,更多信息请看这里。
template<typename T>
class ThreadLocal : boost::noncopyable

一旦某个线程创建了一个key,比如key1,那么其他线程也有自己的key1,它们通过各自的key1访问到的实际数据(堆上内存分配的空间)是不同的,pthread_key_delete 只是删除key,实际数据空间的释放需要在pthread_key_create 中注册一个回调函数destructor去delete T*。
ThreadLocal()
{
pthread_key_create(&pkey_, &ThreadLocal::destructor);
}
当某个线程运行结束,这个线程对应的实际数据T也没有存在的价值,会调用destructor,从而delete T*;
也就是说如果有两个线程,那么destructor会执行两次,因为实际数据T在每个线程中各有一份。
key 的删除在~ThreadLocal()函数内:
~ThreadLocal()
{
pthread_key_delete(pkey_);
}
测试代码:
SingletonThreadLocal_test.cc:
#include <muduo/base/Singleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/Thread.h>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <stdio.h>
class Test : boost::noncopyable
{
public:
Test()
{
printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
}
~Test()
{
printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
}
const std::string &name() const
{
return name_;
}
void setName(const std::string &n)
{
name_ = n;
}
private:
std::string name_;
};
#define STL muduo::Singleton<muduo::ThreadLocal<Test> >::instance().value()
void print()
{
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&STL,
STL.name().c_str());
}
void threadFunc(const char *changeTo)
{
print();
STL.setName(changeTo);
sleep(1);
print();
}
int main()
{
STL.setName("main one");
muduo::Thread t1(boost::bind(threadFunc, "thread1"));
muduo::Thread t2(boost::bind(threadFunc, "thread2"));
t1.start();
t2.start();
t1.join();
print();
t2.join();
pthread_exit(0);
}muduo::Singleton<muduo::ThreadLocal<Test> >::instance() 保证不同线程调用返回的都是同一个ThreadLocal<Test> 对象,
而不同线程调用ThreadLocal<Test>.value(); 返回的是不同的Test对象,即在不同线程中各有一份实际数据。
输出如下:
// 红色为不同Test对象的地址
simba@ubuntu:~/Documents/build/debug/bin$ ./singletonthreadlocal_test
tid=2894, constructing 0x8507038 //main thread
tid=2896, constructing 0xb6200468 // thread2
tid=2896, 0xb6200468 name=
tid=2895, constructing 0xb6300468 // thread1
tid=2895, 0xb6300468 name=
tid=2896, 0xb6200468 name=thread2
tid=2896, destructing 0xb6200468 thread2
tid=2895, 0xb6300468 name=thread1
tid=2895, destructing 0xb6300468 thread1
tid=2894, 0x8507038 name=main one
tid=2894, destructing 0x8507038 main one
simba@ubuntu:~/Documents/build/debug/bin$
二、ThreadLocalSingleton<T>类
template<typename T>
class ThreadLocalSingleton : boost::noncopyable
{
class Deleter { };
};


每个线程都有一个单例对象T,即每个线程都有一个T*(__thread修饰,指针是POD类型数据),每个线程都有各自的特定数据(用TSD实现),t_value_ 即指向实际数据T的指针。
其中instance() 的实现与Singleton 类的实现不同,因为这里是每个线程各有一个单例对象T,而不是所有线程共享一个。
deleter_ 是静态数据成员,为所有线程所共享;t_value_ 虽然也是静态数据成员,但加了__thread 修饰符,故每一个线程都会有一份。Deleter类是用来实现当某个线程执行完毕,执行注册的destructor函数,进而delete t_value_ 。key 的删除在~Deleter() 中
~Deleter()
{
pthread_key_delete(pkey_);
}
测试代码:
ThreadLocalSingleton_test.cc:
#include <muduo/base/ThreadLocalSingleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <stdio.h>
class Test : boost::noncopyable
{
public:
Test()
{
printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
}
~Test()
{
printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
}
const std::string &name() const
{
return name_;
}
void setName(const std::string &n)
{
name_ = n;
}
private:
std::string name_;
};
void threadFunc(const char *changeTo)
{
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::ThreadLocalSingleton<Test>::instance(),
muduo::ThreadLocalSingleton<Test>::instance().name().c_str());
muduo::ThreadLocalSingleton<Test>::instance().setName(changeTo);
sleep(2); //如果没有sleep,可能thread2在thread1退出后才运行,%p可能是原来的值
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::ThreadLocalSingleton<Test>::instance(),
muduo::ThreadLocalSingleton<Test>::instance().name().c_str());
// no need to manually delete it
// muduo::ThreadLocalSingleton<Test>::destroy();
}
int main()
{
muduo::ThreadLocalSingleton<Test>::instance().setName("main one");
muduo::Thread t1(boost::bind(threadFunc, "thread1"));
muduo::Thread t2(boost::bind(threadFunc, "thread2"));
t1.start();
t2.start();
t1.join();
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::ThreadLocalSingleton<Test>::instance(),
muduo::ThreadLocalSingleton<Test>::instance().name().c_str());
t2.join();
pthread_exit(0);
}输出如下:
// 红色为不同Test 对象的地址
simba@ubuntu:~/Documents/build/debug/bin$ ./threadlocalsingleton_test
tid=3341, constructing 0x8a22028 // main thread
tid=3343, constructing 0xb6200468 // thread 2
tid=3343, 0xb6200468 name=
tid=3342, constructing 0xb6000468 // thread 1
tid=3342, 0xb6000468 name=
tid=3343, 0xb6200468 name=thread2
tid=3343, destructing 0xb6200468 thread2
tid=3342, 0xb6000468 name=thread1
tid=3342, destructing 0xb6000468 thread1
tid=3341, 0x8a22028 name=main one
tid=3341, destructing 0x8a22028 main one
参考:
muduo manual.pdf
《linux 多线程服务器编程:使用muduo c++网络库》