版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84325778
线程特定数据 (线程内私有的全局变量)来源
- 在单线程程序中,我们经常要用到“全局变量”以实现多个函数间能共享数据
- 在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程共享
- 但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中生效,但却可以跨多个函数访问
- POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称为Thread-specific Data / TSD
- 线程特定数据也称为本地存储TLS
- 对于POD类型的线程本地存储,可以使用__thread关键字
POSIX线程特定数据
- 一旦有任意一个线程调用了pthread_key_create创建了key,那么所有的线程都拥有自己的key
- 可以为特定线程,设置特定数据,这样不同的线程就会有不同的特定数据(虽然它们的key相同,但是key对应的实际数据不同)
- 怎么销毁线程的特定数据呢?答:在pthread_key_create函数中指定key的销毁的回调函数
muduo实现的线程特定数据ThreadLocal<T>
#ifndef MUDUO_BASE_THREADLOCAL_H
#define MUDUO_BASE_THREADLOCAL_H
#include <muduo/base/Mutex.h> // MCHECK
#include <muduo/base/noncopyable.h>
#include <pthread.h>
namespace muduo
{
template<typename T>
class ThreadLocal : noncopyable
{
private:
pthread_key_t pkey_;
public:
ThreadLocal()
{
//创建线程特定数据pkey_,并指定销毁实际数据的回调函数destructor
MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));
}
~ThreadLocal()
{
MCHECK(pthread_key_delete(pkey_));
}
T& value()
{
//pthread_getspecific:通过pkey_获取线程特定数据对应的实际数据
T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));
if (!perThreadValue) //返回值为空,说明pkey_对应的实际数据还没创建
{
T* newObj = new T(); //就创建pkey_对应的实际数据
MCHECK(pthread_setspecific(pkey_, newObj)); //为pkey_设置对应的实际数据
perThreadValue = newObj;
}
return *perThreadValue;
}
private:
static void destructor(void *x)
{
T* obj = static_cast<T*>(x);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; //检查是否是完全类型
T_must_be_complete_type dummy; (void) dummy;
delete obj;
}
};
测试程序与使用特定数据的方法
使用特定数据的方法
- 将自定义类Test,作为模板参数T传给ThreadLocal,即muduo::ThreadLocal<Test>
- 定义全局的特定数据,即muduo::ThreadLocal<Test> testObj;
此时,所有的线程都拥有变量testObj,只是每个线程中testObj对应的实际数据是不同的 - 在任意一个线程中,可以使用testObj.value()函数获得特定类型对应的实际的Test*,进而使用Test*去访问Test中的数据
示例代码1
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>
#include <stdio.h>
class Test : muduo::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 muduo::string& name() const { return name_; }
void setName(const muduo::string& n) { name_ = n; }
private:
muduo::string name_;
};
//定义线程特定函数,表示每个线程都有自己独立的特定数据
muduo::ThreadLocal<Test> testObj1;
muduo::ThreadLocal<Test> testObj2;
void print()
{
printf("tid=%d, obj1 %p name=%s\n",
muduo::CurrentThread::tid(),
&testObj1.value(),
testObj1.value().name().c_str());
printf("tid=%d, obj2 %p name=%s\n",
muduo::CurrentThread::tid(),
&testObj2.value(),
testObj2.value().name().c_str());
}
void threadFunc()
{
print();
testObj1.value().setName("changed 1");
testObj2.value().setName("changed 42");
print();
}
int main()
{
//主线程
testObj1.value().setName("main one");
print();
//线程1
muduo::Thread t1(threadFunc);
t1.start();
t1.join();
//主线程
testObj2.value().setName("main two");
print();
pthread_exit(0);
}
示例代码2
/*
该示例表达的用意是:
- 所有的线程访问的都是muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象,是相同的
- 但是每个线程中muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象对应的特定数据value()是线程私有的,是不同的
*/
下面代码总结为一句话:所有的线程公用一个实例instance(),但是各个线程拥有自己私有的特定数据instance().value()
#include <muduo/base/Singleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/Thread.h>
#include <stdio.h>
#include <unistd.h>
class Test : muduo::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 muduo::string& name() const { return name_; }
void setName(const muduo::string& n) { name_ = n; }
private:
muduo::string name_;
};
/*
该示例表达的用意是:
1. 所有的线程访问的都是muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象
2. 但是每个线程中muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象对应的
特定数据value()是线程私有的,是不同的
*/
#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(std::bind(threadFunc, "thread1"));
muduo::Thread t2(std::bind(threadFunc, "thread2"));
t1.start();
t2.start();
t1.join();
print();
t2.join();
pthread_exit(0);
}