线程数据处理

来源:互联网 发布:交换机网管软件 编辑:程序博客网 时间:2024/05/01 17:49
和进程相比, 线程的最大优点之一就是数据的共享性, 各个进程共享父进程处沿袭的数据段, 可以方便的获得,修改数据. 但这也给多线程编程带来了很多总是. 我们必须当心有多个不同的进程访问相同的变量. 许多函数是不可重入的, 即同时不能运行一个函数的多个拷贝(除非使用不同的数据段). 在函数中声明的静态变量常常带来问题, 函数的返回值也会有问题. 因为如果返回的是函数内部静态声明的空间的地址, 则在一个线程调用该函数得到地址后使用该地址指向的数据时, 别的线程可能调用些函数并修改了这一段数据. 在进程中共享的变量必须用关键字volatile来定义, 这是为防止编译器在优化时改变它们的使用方式. 为了保护变量, 我们必须使用信号量, 互斥等方法来保证我们的变量的正确使用.


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


创建一个键, extern int pthread_key_create __P( (pthread_key_t * __key, void( * __destr_function)( void *)));
第一个参数为指向一个键值的指针, 第二参数指明了一个destructor函数, 如果这个函数不为空, 那么当线程结束的时候, 系统将调用这个函数来释放绑定在这个键上的内存块. 这个函数常和函数pthread_once((pthread_once_t *once_control, void (* initroutine)( void)))一起使用, 为了让这个键只被创建一次. 函数pthread_once声明一个初始化函数, 第一次调用pthread_once时它执行这个函数, 以后的调用将被它忽略.


下面用一个例子来说明, 创建一个键, 并将它和某个数据相关联系. 我们要定义一个函数createWindow, 这个函数定义一个图形窗口( 数据类型为F1_Window *, 这是图形界面开发工具FLTK中的数据类型). 由于各个线程都会调用这个函数, 所以我们使用线程数据.
/*声明一个键*/
pthread_key_t myWinKey;
/*函数 createWindow*/
void createWindow(void)
{
F1_Window *win;
static pthread_once_t once = PTHREAD_ONCE_INIT;
/*调用函数createMyKey, 创建键*/
pthread_once( & once, createMyKey);
/*win指向一个新建立的窗口*/
win = new F1_Window( 0, 0, 100, 100, "MyWindow");
/*对此窗口作一些可能的设置工作, 如大小, 位置, 名称等*/
setWindow( win);
/*将窗口指针值绑定在键myWinKey上*/
pthread_setpecific( myWinKey, win);
}


/*函数createMyKey, 创建一个键, 并指定了destructor*/
void createMyKey( void)
{
pthread_keycreate( &myWinKey, freeWinKey);
}


/*函数freeWinKey, 释放空间*/
void freeWinKey( F1_Window * win)
{
delete win;
}


这样, 在不同的线程中调用函数createMyWin, 都可以得到在线程内部均可见的窗口变量, 这个变量通过函数pthread_getspecific得到. 用pthread_setspecific来将线程数据和一个键绑定在一起.
extern int pthread_setspecific __P(( pthread_key_t __key, __const void * __pointer));
extern void * pthread_getspecific __P(( pthread_key_t __key));
这两个函数的参数意义和使用方法是显而易见的, 用pthread_setspecific为一个键指定新的线程数据时, 必须自己释放原有的线程数据以回收空间. 这个过程函数pthread_key_delete用来删除一个键, 这个键占用的内存将被释放, 它只释放键占用的内存, 并不释放该键关联的线程数据所占用的内存资源, 而且它也不会触发函数pthread_key_create中定义的destructor函数. 线程数据的释放必须在释放键之前完成.
原创粉丝点击