TSD(Thread Specific Data)线程专有数据

本文探讨了多线程编程中线程专有数据(TSD)的重要性及其实现方法,对比了不同方案,并展示了如何利用pthread库实现TSD。

(1)全局变量

(2)局部变量

(3)TSD(Thread-Specific Data 线程专有数据)

1.http://upczap.itpub.net/

   在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见 的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程 里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。



2.http://bbs.chinaunix.net/viewthread.php?tid=941730

linux 多线程编程中引入了Thread-Specific Data(线程相关的数据)的概念
为什么需要"线程相关的数据"呢?怎样使用"线程相关的数据"呢?

1. 为什么需要Thread-Specific Data "线程相关的数据"

这里只介绍我个人认为的一个原因, 当然它还有许多其他用途,欢迎大家讨论

例子:实现同时运行两个线程,对于每个线程,在该线程调用的每个函数中打印线程的名字,以及它正在调用的函数的名字.

(下面的例子与实现只是为了说明问题,有些地方可能不妥)

不使用"线程相关的数据"的两种实现方法:

实现方法1. 不使用全局变量

#include <string.h>
#include <pthread.h>
#define MAXLENGTH 20

void another_func (const char * threadName)
{
  printf ("%s is running in another_func/n", threadName);
}

void * thread_func (void * args)
{
  char threadName[MAXLENGTH];
  strncpy (threadName, (char *)args, MAXLENGTH-1);
  
  printf ("%s is running in thread_func/n", threadName);
  another_func (threadName);
  
}

int main (int argc, char * argv[])
{

  pthread_t pa, pb;
  pthread_create ( &pa, NULL, thread_func, "Thread A");
  pthread_create ( &pb, NULL, thread_func, "Thread B");

  pthread_join (pa, NULL);
  pthread_join (pb, NULL);
}


输出结果为:
Thread A is running in thread_func
Thread A is running in another_func
Thread B is running in thread_func
Thread B is running in another_func

该方法的缺点是:由于要记录是哪一个线程在调用函数,每个函数需要一个额外的参数来
记录线程的名字,例如another_func函数需要一个threadName参数
如果调用的函数多了,则每一个都需要一个这样的参数

实现方法2. 使用全局变量

#include <string.h>
#include <pthread.h>
#define MAXLENGTH 20

char threadName[MAXLENGTH];
pthread_mutex_t sharedMutex=PTHREAD_MUTEX_INITIALIZER;

void another_func ()
{
  printf ("%s is running in another_func/n", threadName);
}

void * thread_func (void * args)
{
  pthread_mutex_lock(&sharedMutex);
  strncpy (threadName, (char *)args, MAXLENGTH-1);
  printf ("%s is running in thread_func/n", threadName);
  another_func ();
  pthread_mutex_unlock(&sharedMutex);
  
}

int main (int argc, char * argv[])
{

  pthread_t pa, pb;
  pthread_create ( &pa, NULL, thread_func, "Thread A");
  pthread_create ( &pb, NULL, thread_func, "Thread B");

  pthread_join (pa, NULL);
  pthread_join (pb, NULL);
}


该方法的缺点是:由于多个线程需要读写全局变量threadName,就需要使用互斥机制

分析以上两种实现方法,Thread-Specific Data "线程相关的数据"的一个好处就体现出来了:
(1)"线程相关的数据"可以是一个全局变量,并且
(2)每个线程存取的"线程相关的数据"是相互独立的.


2. 怎样使用"线程相关的数据"

这是利用"线程相关的数据"的实现方式:

#include <string.h>
#include <pthread.h>

pthread_key_t p_key;

void another_func ()
{
  printf ("%s is running in another_func/n", (char *)pthread_getspecific(p_key));
}

void * thread_func (void * args)
{
  pthread_setspecific(p_key, args);
  printf ("%s is running in thread_func/n", (char *)pthread_getspecific(p_key));
  another_func ();
  
}

int main (int argc, char * argv[])
{

  pthread_t pa, pb;

  pthread_key_create(&p_key, NULL);
  
  pthread_create ( &pa, NULL, thread_func, "Thread A");
  pthread_create ( &pb, NULL, thread_func, "Thread B");

  pthread_join (pa, NULL);
  pthread_join (pb, NULL);
}


说明:
(1)
线程A, B共用了p_key,
通过p_key,就可以存取只跟当前线程相关的一个值(这个值由编译器管理)
线程A----->p_key----->线程A相关的值(由编译器管理)
线程B----->p_key----->线程B相关的值(由编译器管理)

设置"线程相关的数据",使用
int pthread_setspecific(pthread_key_t key, const void *pointer);
读取"线程相关的数据",使用
void * pthread_getspecific(pthread_key_t key);

注意到,这两个函数分别有一个void类型的指针,我们的线程就是通过这两个指针分别与
"线程相关的数据"的数据进行交互的

(2)
由于p_key是一个全局变量,
函数another_func不需要额外的参数就可以访问它;
又因为它是"线程相关的数据", 线程A, B通过p_key存取的数据是相互独立的,
这样就不需要额外的互斥机制来保证数据访问的正确性了.

<think>嗯,用户想了解线程间私有数据的概念、作用和实现方式。我需要先回忆一下线程的基础知识。线程之间通常共享进程的资源,比如全局变量和堆内存,这可能导致数据竞争和同步问题。这时候,线程私有数据TSD)就派上用场了,它允许每个线程拥有独立的数据副本,避免竞争。 根据引用[3],TSD使用一键多值的技术,一个键对应不同线程中的不同数据。这样,每个线程通过相同的键访问自己的私有数据。实现方式可能涉及创建和销毁键,以及关联数据到键上。引用[5]提到TSD是同key不同value,访问通过键来获取各自的值。 接下来是具体实现步骤。首先需要创建键,比如使用pthread_key_create函数。然后,每个线程可以用pthread_setspecific数据关联到键,用pthread_getspecific获取数据。销毁键的时候用pthread_key_delete,但要注意先释放每个线程数据,避免内存泄漏。 作用方面,TSD主要用于避免竞争,比如将errno设为线程私有,或者维护线程的独立上下文,如数据库连接。引用[4]提到线程池中使用TSD可以实现无竞争的内存申请,说明实际应用中的有效性。 需要注意线程退出时的资源释放,可能需要析构函数。另外,键的创建应在所有线程使用之前,通常在主线程初始化时完成。需要确保这些步骤正确,避免数据错乱或泄漏。 用户可能的问题会涉及如何具体使用这些函数,TSD的应用场景,或者与其他同步机制的区别。需要准备好这些相关问题的回答,帮助用户深入理解。</think>### 线程间私有数据的作用与实现方式 #### 一、线程私有数据的作用 线程私有数据Thread-Specific Data, TSD)是一种允许**每个线程拥有独立数据副本**的机制,其核心作用包括: 1. **避免数据竞争**:在多线程环境中,全局变量或静态变量会被所有线程共享,可能导致数据竞争。TSD通过为每个线程提供独立的数据副本,消除竞争条件[^3][^5]。 2. **维护线程上下文**:某些场景需要线程保存自身状态(如错误码`errno`、数据库连接句柄等),TSD可实现线程数据的隔离[^2][^4]。 3. **兼容旧代码**:在将单线程代码改造为多线程时,TSD可隔离原本依赖全局变量的逻辑,减少代码改动[^3]。 #### 二、实现方式 TSD的实现基于**一键多值**技术:一个全局的键(key)关联到不同线程的私有数据值(value)。以下是关键步骤: 1. **创建线程私有数据键** 使用`pthread_key_create`创建全局唯一的键,并为该键注册析构函数(用于线程退出时释放资源): ```c pthread_key_t key; pthread_key_create(&key, destructor_function); ``` 2. **关联数据到当前线程** 通过`pthread_setspecific`将数据与当前线程绑定: ```c void *data = malloc(sizeof(my_struct)); pthread_setspecific(key, data); // 将数据关联到当前线程的键 ``` 3. **读取线程私有数据** 使用`pthread_getspecific`获取当前线程关联的数据: ```c void *data = pthread_getspecific(key); // 仅访问当前线程数据 ``` 4. **销毁键** 在所有线程释放数据后,调用`pthread_key_delete`销毁键: ```c pthread_key_delete(key); ``` #### 三、应用示例 以下代码演示了线程私有数据的典型用法(以错误码隔离为例): ```c #include <pthread.h> #include <stdlib.h> static pthread_key_t errno_key; void destructor(void *data) { free(data); // 线程退出时释放错误码内存 } void init_errno_key() { pthread_key_create(&errno_key, destructor); } int *get_thread_errno() { int *err = pthread_getspecific(errno_key); if (!err) { err = malloc(sizeof(int)); *err = 0; pthread_setspecific(errno_key, err); } return err; } ``` #### 四、注意事项 - **键的初始化**:键的创建(`pthread_key_create`)应在所有线程使用前完成,通常由主线程初始化[^2]。 - **内存管理**:需手动管理线程私有数据的内存,析构函数负责释放资源。 - **线程安全**:键的创建和删除本身非线程安全,需配合锁或单次初始化机制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值