- 浏览: 142433 次
文章分类
最新评论
线程特定数据也称线程私有数据,是存储和查询某个特定线程相关数据的一种机制。在分配线程特定数据之前,需要创建与该数据相关联的键,以用于获取对线程特定数据的访问。使用函数 pthread_key_create 可创建一个键,而对所有的线程,都可以通过 pthread_key_delete 来取消键与线程特定数据值之间的关联关系。
创建的键存储在 keyp 指向的内存单元中,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程特定数据地址进行关联。创建新键时,每个线程的数据地址都设为空值。除了创建键以外,pthread_key_create 还可以为该键关联一个可选择的析构函数。当这个线程退出时,如果数据地址已经被置为非空值,那么析构函数就会被调用,它唯一的参数就是该数据地址。当线程调用 pthread_exit 或者返回时,析构函数就会被调用;而当线程取消时,只有在最后的清理处理程序返回后,析构函数才会被调用。线程通常使用 malloc 为线程特定数据分配内存,所以析构函数经常调用 free 函数来释放已分配的内存,以免产生内存泄露。线程可以为线程特定数据分配多个键,每个键都可以关联一个析构函数。线程退出时,线程特定数据的析构函数将按照操作系统实现中定义的顺序被调用。当所有的析构函数都调用完成后,系统会检查是否还有非空的线程特定数据值与键关联。如果有的话,会再次调用析构函数。该过程会一直重复直到线程所有的键都为空线程特定数据值,或者已经做了 PTHREAD_DESTRUCTOR_ITERATIONS 次尝试。
调用 pthread_key_delete 并不会激活与键关联的析构函数,要释放任何与键关联的线程特定数据值的内存,需要在应用程序中采取额外的步骤。
为确保分配的键不会因初始化阶段的竞争而发生变动,可以使用函数 pthread_once。键一旦创建以后,就可以通过 pthread_setspecific 函数把键和线程特定数据关联起来,也可通过 pthread_getspecific 函数获得线程特定数据的地址。
这里的 initflag 必须是一个非本地变量(如全局变量或静态变量),而且必须初始化为 PTHREAD_ONCE_INIT。就算每个线程都调用 pthread_once,系统也能保证初始化例程 initfn 只被调用一次,这可以避免创建键时出现冲突。
下面给出了 getenv 的一个假设实现,它使用线程特定数据来维护每个线程的数据缓冲区副本,用于存放各自的返回字符串。
这里使用 pthread_once 来确保只为将要使用的线程特定数据创建一个键。如果 pthread_getspecific 返回的是空指针,就需要先分配内存缓冲区,然后再把键与该内存缓冲区关联起来,否则就使用返回的内存缓冲区。对析构函数,使用 free 来释放之前由 malloc 分配的内存。只有当线程特定数据值为非空时,析构函数才会被调用。
注意,相较于线程重入中实现的 getenv_r 版本,虽然该版本的 genenv 也使用了互斥量来保证线程安全,但它并不是异步信号安全的。对信号处理程序而言,即使使用递归的互斥量,该版本也不可能是可重入的,因为它调用了本身就不是异步信号安全的 malloc 函数。
#include <pthread.h> int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *)); int pthread_key_delete(pthread_key_t key); /* 两个函数返回值:若成功,返回 0;否则,返回错误编号 */
创建的键存储在 keyp 指向的内存单元中,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程特定数据地址进行关联。创建新键时,每个线程的数据地址都设为空值。除了创建键以外,pthread_key_create 还可以为该键关联一个可选择的析构函数。当这个线程退出时,如果数据地址已经被置为非空值,那么析构函数就会被调用,它唯一的参数就是该数据地址。当线程调用 pthread_exit 或者返回时,析构函数就会被调用;而当线程取消时,只有在最后的清理处理程序返回后,析构函数才会被调用。线程通常使用 malloc 为线程特定数据分配内存,所以析构函数经常调用 free 函数来释放已分配的内存,以免产生内存泄露。线程可以为线程特定数据分配多个键,每个键都可以关联一个析构函数。线程退出时,线程特定数据的析构函数将按照操作系统实现中定义的顺序被调用。当所有的析构函数都调用完成后,系统会检查是否还有非空的线程特定数据值与键关联。如果有的话,会再次调用析构函数。该过程会一直重复直到线程所有的键都为空线程特定数据值,或者已经做了 PTHREAD_DESTRUCTOR_ITERATIONS 次尝试。
调用 pthread_key_delete 并不会激活与键关联的析构函数,要释放任何与键关联的线程特定数据值的内存,需要在应用程序中采取额外的步骤。
为确保分配的键不会因初始化阶段的竞争而发生变动,可以使用函数 pthread_once。键一旦创建以后,就可以通过 pthread_setspecific 函数把键和线程特定数据关联起来,也可通过 pthread_getspecific 函数获得线程特定数据的地址。
#include <pthread.h> pthread_once_t initflag = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); int pthread_setspecific(pthread_key_t key, const void *value); /* 两个函数返回值:若成功,返回 0;否则,返回错误编号 */ void *pthread_getspecific(pthread_key_t key); /* 返回值:线程特定数据值;若没有值与该键关联,返回 NULL */
这里的 initflag 必须是一个非本地变量(如全局变量或静态变量),而且必须初始化为 PTHREAD_ONCE_INIT。就算每个线程都调用 pthread_once,系统也能保证初始化例程 initfn 只被调用一次,这可以避免创建键时出现冲突。
下面给出了 getenv 的一个假设实现,它使用线程特定数据来维护每个线程的数据缓冲区副本,用于存放各自的返回字符串。
#include <stdlib.h> #include <string.h> #include <pthread.h> static pthread_key_t key; static pthread_once_t init_done = PTHREAD_ONCE_INIT; static pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER; static void key_init(void){ pthread_key_create(&key, free); } #define MAXSTRINGSZ 4096 extern char **environ; char *getenv(const char *name){ pthread_once(&init_done, key_init); pthread_mutex_lock(&env_mutex); char *env_buf = NULL; if((env_buf=(char *)pthread_getspecific(key)) == NULL){ if((env_buf=malloc(MAXSTRINGSZ)) == NULL){ pthread_mutex_unlock(&env_mutex); return NULL; } pthread_setspecific(key, env_buf); } int i = 0, len = strlen(name); for(; environ[i] != NULL; i++){ if(strncmp(name, environ[i], len)==0 && environ[i][len]=='='){ strncpy(env_buf, &environ[i][len+1], MAXSTRINGSZ-1); pthread_mutex_unlock(&env_mutex); return env_buf; } } pthread_mutex_unlock(&env_mutex); return NULL; }
这里使用 pthread_once 来确保只为将要使用的线程特定数据创建一个键。如果 pthread_getspecific 返回的是空指针,就需要先分配内存缓冲区,然后再把键与该内存缓冲区关联起来,否则就使用返回的内存缓冲区。对析构函数,使用 free 来释放之前由 malloc 分配的内存。只有当线程特定数据值为非空时,析构函数才会被调用。
注意,相较于线程重入中实现的 getenv_r 版本,虽然该版本的 genenv 也使用了互斥量来保证线程安全,但它并不是异步信号安全的。对信号处理程序而言,即使使用递归的互斥量,该版本也不可能是可重入的,因为它调用了本身就不是异步信号安全的 malloc 函数。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1252在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1550伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 800多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 950终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 569尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 944虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 710tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 815终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 439终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 579在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 947在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 617在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 497消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 485有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 421FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 389管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 889readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 455POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 615记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 439UNIX 系统中,守护进程遵循下列通用惯例。 ...
相关推荐
### 线程特定数据(Thread-Specific Data, TSD)在Linux下的应用 #### 一、背景介绍 在多线程编程环境下,全局变量和静态变量的使用常常会导致不可预知的结果,尤其是在多线程并发访问的情况下。这是因为全局变量...
4. **使用线程句柄**:易语言允许通过线程句柄发送消息,线程执行完毕后,可以通过发送特定消息将数据返回。 5. **回调函数**:线程执行过程中,可以设定一个回调函数,当线程执行完毕,将数据通过回调函数返回。 ...
pthread_setspecific 函数用于设置线程特定数据,该函数需要两个参数:线程特定数据键和线程特定数据值。 4. 获取线程特定数据 pthread_getspecific 函数用于获取线程特定数据,该函数需要一个参数:线程特定数据...
- 删除不再需要的线程特定数据键。 #### 三、设置线程特定数据 **获取线程特定数据** - 如何访问线程特有的数据。 **获取线程标识符** - 获取当前线程的唯一标识符。 **比较线程ID** - 如何比较两个线程ID...
- **为线程特定数据创建键**:讲解了如何为线程分配特定的数据空间,以便每个线程拥有自己的数据副本。 **3. 设置线程特定数据** - **获取线程特定数据**:说明了如何访问线程内的特定数据。 - **获取线程标识符**...
为了解决这个问题,我们可以利用C#中的多线程技术来实现后台处理数据,从而保持UI的响应性。本篇将详细讨论如何在WinForm应用中运用多线程处理数据以及异步更新UI。 1. **什么是多线程?** 在计算机科学中,多线程...
- `pthread_key_delete`用于删除之前创建的线程特定数据键。 ##### 设置线程特定数据 - 使用`pthread_setspecific`函数将特定于线程的数据与键关联起来。 #### 三、获取线程特定数据 - 可以通过`pthread_get...
线程特定数据(TSD)是仅对创建它的线程可见的数据,通过TSD可以避免全局变量的竞态条件问题。 ##### 获取线程特定数据 使用`pthread_getspecific`函数可以获取线程特定数据。 ##### 获取线程标识符 使用`pthread_...
在易语言中,可以使用“锁定资源”和“解锁资源”来确保同一时间只有一个线程访问特定的数据,避免数据不一致。 此外,标签“SanYe”可能指的是易语言开发者“三叶草”,他是一个知名的易语言教程作者和社区贡献者...
线程的分离属性可以指定线程退出时是否自动释放其资源,而线程特定数据允许每个线程拥有自己的数据副本,这些数据对于其他线程是不可见的。 线程属性在pthread中是一个重要的概念,包括线程的分离状态、优先级、栈...
POSIX线程库(pthread)为Linux下的多线程编程提供了一套完整的工具集,包括线程创建与管理、线程间同步(如互斥锁和条件变量)、线程特定数据、线程取消与终止等高级功能。掌握这些知识不仅能够帮助开发者构建高...
- **线程特定数据(TSD)**: 为每个线程分配独立的数据存储空间。 - **创建TSD键**: 使用`pthread_key_create()`函数为TSD创建键。 - **删除线程特定数据键** - **删除TSD键**: 使用`pthread_key_delete()`函数...
- **设置线程特定数据**:使用`pthread_setspecific()`函数为指定的键设置特定于当前线程的数据值。 #### 三、高级线程管理 ##### 获取线程标识符 - **线程ID**:每个线程都有一个唯一的标识符,称为线程ID。 - *...
- **获取线程特定数据**: 通过`pthread_getspecific`函数获取与某个键关联的数据。 - **获取线程标识符**: 使用`pthread_self`函数获取当前线程的标识符。 - **比较线程ID**: 通过直接比较线程标识符来进行。 - **...
- **为线程特定数据创建键**: 创建一个键以存储线程特定的数据。 - **删除线程特定数据键**: 删除不再需要的键。 - **设置线程特定数据**: 将数据关联到特定线程。 **2.2 获取线程特定数据** - **获取线程标识符**...