我们知道,不管是在单线程还是在多线程中,全局变量都可以在各线程中共享数据。但当我们需要在各线程中保存各自线程的数据时,该如何处理呢?是在各线程中各自定义数据呢,还是有其他办法呢?pthread 线程库就有这样一种数据结构, 相同的数据名称在不同的线程中各自维护着各自的数据,称为线程私有数据(Thread Specific Data TSD)或称为 线程本地存储 TSS(Thread-Specific Storage), TLS(Thread Local Storage)。而 pthread 线程库一共提供了 4 个接口用来创建、删除、设置、获取 这个线程私有数据,分别为:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); //第 2 个参数是一个函数指针,用来在线程退出时将key值作为参数进行调用。(这个参数暂时没用过)
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
类型 pthread_key_t 实际定义为:
/* Keys for thread-specific data */
typedef unsigned int pthread_key_t;
我们可以用一个类进行封装,如下:
/************************************
*
* filename: ThreadSpecific.h
*
************************************/
#ifndef __THREAD_SPECIFIC_H__
#define __THREAD_SPECIFIC_H__
#include <pthread.h>
class CThreadSpecific
{
public:
CThreadSpecific();
~CThreadSpecific();
// 设置私有数据
bool setValue(void const *value);
// 获取私有数据
bool getValue(void **value);
private:
bool mValid; //数据是否可用,即调用 pthread_key_create 过了
pthread_key_t mKey;
};
#endif
/************************************
*
* filename: ThreadSpecific.cpp
*
************************************/
#include "ThreadSpecific.h"
CThreadSpecific::CThreadSpecific(): mValid(false), mKey(0)
{
mValid = (0 == pthread_key_create(&mKey, NULL));
}
CThreadSpecific::~CThreadSpecific()
{
if(mValid)
{
pthread_key_delete(mKey);
}
}
bool CThreadSpecific::setValue(void const *value)
{
if(mValid)
{
return 0 == pthread_setspecific(mKey, value);
}
return false;
}
bool CThreadSpecific::getValue(void **value)
{
if(mValid)
{
*value = pthread_getspecific(mKey);
return true;
}
return false;
}
那线程私有数据有什么用呢?毫无疑问是用在多线程情况下,比如场景:一台嵌入式设备可以通过局域网进行登录,登录后可以进行配置操作或其他操作,而同时它是允许多个用户操作的,这时多个用户操作其实就是多个线程在操作,那么各个用户(线程)获取到的返回码肯定是要不一样的。假如用一个全局变量会发生什么样的情况呢?可能是:前一个用户操作错误时,紧接着一个用户操作没有错误,而取到是前一个用户操作的错误码,这样是不是混乱了呢?所以这种情况下,各个线程操作的结果应该是各个线程进行私有设置,这样各个线程获取到的错误码才是正确的。
那实际使用时可以再添加一个管理错误码的类,实现为一个单例,然后提供设置和获取错误码的接口,类似如下:
/****************************
* filename: ErrorManager.h
***************************/
#ifndef __ERROR_MANAGER_H__
#define __ERROR_MANAGER_H__
#include "ThreadSpecific.h"
class CErrorManager
{
private:
CErrorManager()
{}
~CErrorManager()
{}
CErrorManager(const CErrorManager&) = delete;
CErrorManager& operator=(const CErrorManager&) = delete;
public:
static CErrorManager *instance();
bool setLastError(int errno);
int getLastError();
};
#endif
/*******************************
* filename: ErrorManager.cpp
******************************/
#include <stdint.h>
#include "ErrorManager.h"
static CThreadSpecific sTSSLastError;
CErrorManager *CErrorManager::instance()
{
static CErrorManager sInstance;
return &sInstance;
}
bool CErrorManager::setLastError(int errno)
{
return sTSSLastError.setValue(reinterpret_cast<void const*>(errno));
}
int CErrorManager::getLastError()
{
void *value = NULL;
sTSSLastError.getValue(&value);
return ((int)reinterpret_cast<intptr_t>(value));
}
main 函数如下,创建两个线程,用简单的 sleep() 方式先后启动线程函数,同时交叉地设置了各自的线程私有数据,然后再获取。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
#include "ErrorManager.h"
void *threadProc(void *arg)
{
int errNo = *(int *)arg;
printf("tid[%ld] setLastError %d to threadspecific!\n", syscall(SYS_gettid), errNo);
CErrorManager::instance()->setLastError(errNo);
sleep(3);
int value = CErrorManager::instance()->getLastError();
printf("tid[%ld] getLastError %d from threadspecific!\n", syscall(SYS_gettid), value);
return nullptr;
}
int main()
{
pthread_t tid1, tid2;
int value = 200;
pthread_create(&tid1, nullptr, threadProc, (void *)&value);
sleep(1);
value = 500;
pthread_create(&tid2, nullptr, threadProc, (void *)&value);
pthread_join(tid1, nullptr);
pthread_join(tid2, nullptr);
return 0;
}
其结果为:
各个线程获取的都是各自设置的数据。