我们知道,不管是在单线程还是在多线程中,全局变量都可以在各线程中共享数据。但当我们需要在各线程中保存各自线程的数据时,该如何处理呢?是在各线程中各自定义数据呢,还是有其他办法呢?pthread 线程库就有这样一种数据结构, 相同的数据名称在不同的线程中各自维护着各自的数据,称为线程私有数据(Thread Specific Data TSD)或称为 线程本地存储 TSS(Thread-Specific Storage), TLS(Thread Local Storage)。而 pthread 线程库一共提供了 4 个接口用来创建、删除、设置、获取 这个线程私有数据,分别为:
#include
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
-
- 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
- #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
- #include
- #include
- #include
- #include
- #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;
- }
其结果为:

各个线程获取的都是各自设置的数据。