linux多线程学习笔记六--一次性初始化和线程私有数据【转】
转自:http://blog.csdn.net/kkxgx/article/details/7513278
版权声明:本文为博主原创文章,未经博主允许不得转载。
一,一次性初始化
以保证线程在调用资源时,确保资源已经被初始化,并且只初始化一次。
在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理。控制变量被静态初始化为0,而任何依赖于初始化的代码都能测试该变量。如果变量值仍然为0,则它能实行初始化,然后将变量置为1。以后检查的代码将跳过初始化。
但是在多线程程序设计中,事情就变的复杂的多。如果多个线程并发地执行初始化序列代码,2个线程可能发现控制变量为0,并且都实行初始话,而该过程本该仅仅执行一次。初始化的状态必须由互斥量保护。之所以使用pthread_once,主要原因是原来不能静态的初始化一个互斥量,这样如果要使用一个互斥量,必须调用pthread_mutex_init函数初始化互斥量,并且必须仅仅初始化一次,因此初始化调用应该在一次性初始化代码中完成,pthread_once就解决在多线程环境中使得互斥量和初始化代码都仅仅被初始化一次的问题;
[cpp] view plain copy
    #include <pthread.h>
    pthread_once_t once_control=PTHREAD_ONCE_INIT;
    int pthread_once(pthread_once_t *once_control,void(*init_routine)(void));
    init_routine          初始化函数  
实例代码如下:
[cpp] view plain copy
    #include <pthread.h>
    #include <stdio.h>
    pthread_once_t once_block = PTHREAD_ONCE_INIT;
    pthread_mutex_t mutex;  
    /*
     * This is the one-time initialization routine. It will be
     * called exactly once, no matter how many calls to pthread_once
     * with the same control structure are made during the course of
     * the program.
     */
    void once_init_routine (void)
    {
        int status;  
        status = pthread_mutex_init (&mutex, NULL);
        if (status != )
           printf("error Init Mutex");
    }  
    /*
     * Thread start routine that calls pthread_once.
     */
    void *thread_routine (void *arg)
    {
        int status;  
        status = pthread_once (&once_block, once_init_routine);
        if (status != )
            printf("error Once init");
        status = pthread_mutex_lock (&mutex);
        if (status != )
            printf("error Lock mutex");
        printf ("thread_routine has locked the mutex.\n");
        status = pthread_mutex_unlock (&mutex);
        if (status != )
            printf("error Unlock mutex");
        return NULL;
    }  
    int main (int argc, char *argv[])
    {
        pthread_t thread_id;
        char *input, buffer[];
        int status;  
        status = pthread_create (&thread_id, NULL, thread_routine, NULL);
        if (status != )
            printf("error Create thread");
        status = pthread_once (&once_block, once_init_routine);
        if (status != )
            printf("error Once init");
        status = pthread_mutex_lock (&mutex);
        if (status != )
            printf("error Lock mutex");
        printf ("Main has locked the mutex.\n");
        status = pthread_mutex_unlock (&mutex);
        if (status != )
            printf("error Unlock mutex");
        status = pthread_join (thread_id, NULL);
        if (status != )
            printf("error Join thread");
        return ;
    }  
程序输出:
Main has locked the mutex.
thread_routine has locked the mutex.
二,线程私有数据
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。
)建立线程私有数据
[cpp] view plain copy
    pthread_key_t key;
    int pthread_key_create(pthread_key *key,void (*destructor)(void*));
    int pthread_key_delete(pthread_key_t key);  
该函数从TSD池中分配一项,将其值赋给key供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量.
pthread_key_delete这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。因为只有你肯定没有线程持有该键值时,才能删除线程私有数据键,故通常的做法是不释放线程私有数据键。
)使用线程私有数据
[cpp] view plain copy
    int  pthread_setspecific(pthread_key_t  key,  const   void  *value)
    void * pthread_getspecific(pthread_key_t key)  
可以使用pthread_getspecific函数来获取线程当前键值,或通过第一个函数改变当前的键值。
[cpp] view plain copy
    /*
     * tsd_once.c
     *
     * Demonstrate use of pthread_once to initialize something
     * exactly once within a multithreaded program.
     *
     * Note that it is often easier to use a statically initialized
     * mutex to accomplish the same result.
     */
    #include <pthread.h>
    #include <stdio.h>  
    /*
     * Structure used as the value for thread-specific data key.
     */
    typedef struct tsd_tag {
        pthread_t   thread_id;
        char        *string;
    } tsd_t;  
    pthread_key_t tsd_key;           /* Thread-specific data key */
    pthread_once_t key_once = PTHREAD_ONCE_INIT;  
    /*
     * One-time initialization routine used with the pthread_once
     * control block.
     */
    void once_routine (void)
    {
        int status;  
        printf ("initializing key\n");
        status = pthread_key_create (&tsd_key, NULL);
        if (status != )
            printf("error Create key");
    }
    /*
     * Thread start routine that uses pthread_once to dynamically
     * create a thread-specific data key.
     */
    void *thread_routine (void *arg)
    {
        tsd_t *value;
        int status;  
        status = pthread_once (&key_once, once_routine);//一次性初始化
        if (status != )
            printf("error Once init");
        value = (tsd_t*)malloc (sizeof (tsd_t));
        if (value == NULL)
            printf("error Allocate key value");
        status = pthread_setspecific (tsd_key, value);
        if (status != )
            printf("error Set tsd");
        printf ("%s set tsd value %p\n", arg, value);
        value->thread_id = pthread_self ();
        value->string = (char*)arg;
        value = (tsd_t*)pthread_getspecific (tsd_key);
        printf ("%s starting...\n", value->string);
        sleep ();
        value = (tsd_t*)pthread_getspecific (tsd_key);
        printf ("%s done...\n", value->string);
        return NULL;
    }  
    void main (int argc, char *argv[])
    {
        pthread_t thread1, thread2;
        int status;  
        status = pthread_create (
            &thread1, NULL, thread_routine, "thread 1");
        if (status != )
            printf("error Create thread 1");
        status = pthread_create (
            &thread2, NULL, thread_routine, "thread 2");
        if (status != )
            printf("error Create thread 2");
        pthread_exit;
    }  
运行结果如下:
thread  set tsd value 0x9de24f0
thread  starting...
thread  set tsd value 0x9de2500
thread  starting...
thread  done...
thread  done...
部分内容摘自:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/
            http://blog.chinaunix.net/uid-20627834-id-3005052.html
linux多线程学习笔记六--一次性初始化和线程私有数据【转】的更多相关文章
- 多线程学习笔记六之并发工具类CountDownLatch和CyclicBarrier
		
目录 简介 CountDownLatch 示例 实现分析 CountDownLatch与Thread.join() CyclicBarrier 实现分析 CountDownLatch和CyclicBa ...
 - linux多线程学习笔记五--线程安全【转】
		
转自:http://blog.csdn.net/kkxgx/article/details/7506085 版权声明:本文为博主原创文章,未经博主允许不得转载. 一,线程安全基础 一个函数被称为线程安 ...
 - linux初级学习笔记六:linux用户及权限详解!(视频序号:03_4)
		
本节学习的命令:/etc/passwd,/etc/shadow,/etc/group文件详解 本节学习的技能: 安全上下文 文件与目录的权限管理 影子命令 用户,用户组类别详解 /etc/passwd ...
 - Docker学习笔记六:Docker搭建企业级私有仓库
		
前言 Docker不仅是一个强大的服务器部署工具,而且它还有一个官方的Docker Hub registry用于储存Docker镜像.上传镜像到Docker Hub是免费的,上传的镜像文件同时也对公共 ...
 - SpringMVC学习笔记六:使用 hibernate-validator注解式数据校验
		
对客户端传过来的参数,在使用前一般需要进行校验. SpringMVC框架内置了Validator验证接口,但是实现起来太麻烦.我们一般使用 hibernate-validator进行数据校验. 1:j ...
 - Linux学习笔记(六) 进程管理
		
1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...
 - 尚硅谷韩顺平Linux教程学习笔记
		
目录 尚硅谷韩顺平Linux教程学习笔记 写在前面 虚拟机 Linux目录结构 远程登录Linux系统 vi和vim编辑器 关机.重启和用户登录注销 用户管理 实用指令 组管理和权限管理 定时任务调度 ...
 - java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域)  (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
		
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
 - 多线程学习笔记九之ThreadLocal
		
目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...
 
随机推荐
- 作业hashlib题目
			
'''1.编写用户认证功能,要求如下 1.1.对用户密码加盐处理 1.2.用户名与密文密码存成字典,是以json格式存到文件中的 1.3.要求密用户输入明文密码,但程序中验证的是密文'''import ...
 - LigerUI 快速开发UI框架 链接
			
LigerUI 快速开发UI框架 http://www.ligerui.com/ jQuery ligerUI 中文官方网站 http://www.ligerui.com/demo.html
 - tp5 修改自带success或error跳转模板页面
			
tp5 修改自带success或error跳转模板页面 我们在使用tp5或者tp3.2的时候,用的成功或者失败跳转提示页面一般是用框架的.在后续开发过程中,根据实际项目需要,也是可以更改的,在此分享一 ...
 - php五种常见的设计模式
			
工厂模式 工厂模式是最常用的实例化对象的模式,是用工厂方法代替new操作的一种模式 使用工厂模式的好处是:如果想要更改实例化的类名,则只需要更改该工厂方法内容即可,不需逐一寻找代码中具体实例化的地方( ...
 - 用iTerm快速链接远程服务器
			
通常情况下,iTerm2访问远程Linux使用ssh ssh <用户名>@<ip> 然后输入访问的密码即可.当然还有的时候需要指定访问端口. ssh -p <端口号> ...
 - 关于欧几里德算法(gcd)的证明
			
求a,b的最大公约数我们经常用欧几里得算法解决,也称辗转相除法, 代码很简短, int gcd(int a,int b){ return (b==0)?a:gcd(b,a%b); } 但其中的道理却很 ...
 - Eclipse快速输出System.out.println();
			
借鉴网上大佬博客 刚开始还好好敲代码 后来看博客发现其实输入syso或sysout 再按alt+/就OK 开始学JAVA,好好干.
 - Hyper-V 网络虚拟化技术细节
			
Hyper-V 网络虚拟化技术细节 适用对象:Windows Server 2012 R2 服务器虚拟化能让多个服务器实例在同一台物理主机上同步运行,但各个服务器实例都是相互独立的. 每台虚拟机的运作 ...
 - 一篇文章看懂Facebook和新浪微博的智能FEED
			
本文来自网易云社区 作者:孙镍波 众所周知,新浪微博的首页动态流不像微信朋友圈是按照时间顺序排列的,而是按照一种所谓的"智能排序"的方式.这种违背了用户习惯的排序方式一直被用户骂, ...
 - C#入门篇-4:使用运算符
			
using System; using System.Text; using System.Collections; using System.Collections.Generic; using S ...