Linux多线程5-4_线程私有数据

简介: 一、什么是私有数据     应用程序设计中有必要提供一种变量,使得多个函数多个线程都可以访问这个变量(看起来是个全局变量),但是线程对这个变量的访问都不     会彼此产生影响(貌似不是全局变量哦),但是你需要这样的数据,比如errno。
一、什么是私有数据
    应用程序设计中有必要提供一种变量,使得多个函数多个线程都可以访问这个变量(看起来是个全局变量),但是线程对这个变量的访问都不
    会彼此产生影响(貌似不是全局变量哦),但是你需要这样的数据,比如errno。那么这种数据就是线程的私有数据,尽管名字相同,但是每个
    线程访问的都是数据的副本。

二、如何创建 私有数据
    1、在使用私有数据之前,你首先要创建一个与私有数据相关的键,要来获取对私有数据的访问权限 。这个键的类型是pthread_key_t
    int pthread_key_create(pthread_key_t *key, void (*destructor)(voi8d*));
    2、创建的键放在key指向的内存单元,destructor是与键相关的析构函数。当线程调用pthread_exit或者使用return返回,析构函数就会被调用。
    当析构函数调用的时候,它只有一个参数,这个参数是与key关联的那个数据的地址(也就是你的私有数据啦),因此你可以在析构函数中将
    这个数据销毁。
    3、键使用完之后也可以销毁,当键销毁之后,与它关联的数据并没有销毁哦
    int pthread_key_delete(pthread_key_t key);

三、如何使用私有数据
    有了键之后,你就可以将私有数据和键关联起来,这样就就可以通过键来找到数据。所有的线程都可以访问这个键,但他们可以为键关联
    不同的数据。(这岂不是一个名字一样,而值却不同的全局变量么)
     1、int pthread_setspecific(pthread_key_t key, const void *value);
         将私有数据与key关联
     2、void *pthread_getspecific(pthread_key_t key);
         获取私有数据的地址,如果没有数据与key关联,那么返回空

四、手册
PTHREAD_KEY_CREATE(3P)     POSIX Programmer’s Manual    PTHREAD_KEY_CREATE(3P)
PROLOG
       This manual page is part of the POSIX Programmer’s Manual.  The Linux implementation of this interface may
       differ (consult the corresponding Linux manual page for details of Linux behavior), or the  interface  may
       not be implemented on Linux.
         //这只是POSIX的手册,Linux对这个接口的实现可能不一样,或者有的根本没有实现这个接口

NAME
       pthread_key_create - thread-specific data key creation
        //创建线程特殊数据

SYNOPSIS
       #include
        //头文件

       int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

DESCRIPTION
       The  pthread_key_create()  function  shall create a thread-specific data key visible to all threads in the
       process. Key values provided by pthread_key_create() are opaque objects  used  to  locate  thread-specific
       data.  Although  the  same  key  value  may  be  used by different threads, the values bound to the key by
       pthread_setspecific() are maintained on a per-thread basis and persist for the life of the calling thread.
        // pthread_key_create()创造一个线程特殊的数据键,所有的线程都能使用它。key 就是用来存放线程私有数据的
        //尽管同一个key的名字被多个线程使用,但是每一个线程都通过 pthread_setspecific()绑定自己的数据

       Upon  key creation, the value NULL shall be associated with the new key in all active threads. Upon thread
       creation, the value NULL shall be associated with all defined keys in the new thread.
        //刚创建key的时候,对于所有的线程来说,它的值都是空的。刚创建线程的时候,线程里所有的key都是空的

       An optional destructor function may be associated with each key value.  At thread exit, if a key value has
       a  non-NULL destructor pointer, and the thread has a non-NULL value associated with that key, the value of
       the key is set to NULL, and then the function pointed to is called with the previously associated value as
       its  sole  argument. The order of destructor calls is unspecified if more than one destructor exists for a
       thread when it exits.
        //每个key可以配置一个析构函数。当线程退出的时候,如果key的析构函数不为NULL,而且key也不是NULL,那么
        //key会被置NULL,同时析构函数被调用。如果有多个析构函数,那么析构函数的调用顺序是不确定的

       If, after all the destructors have been called for all non-NULL values with associated destructors,  there
       are  still  some  non-NULL values with associated destructors, then the process is repeated.  If, after at
       least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor  calls  for  outstanding  non-NULL  values,
       there  are  still  some  non-NULL  values  with  associated  destructors, implementations may stop calling
       destructors, or they may continue calling destructors until no non-NULL values with associated destructors
       exist, even though this might result in an infinite loop.
         //如果所有的析构函数都被调用了,但是还有key的值不为空,那么进程会重复调用析构函数。如果至少有
        // {PTHREAD_DESTRUCTOR_ITERATIONS次的 析构函数被调用了,但是还有非空的key,那么实现者应该去结束
        //析构函数,否则他们会一直调用,甚至可能陷入四循环

RETURN VALUE
       If successful, the pthread_key_create() function shall store the newly created key value at *key and shall
       return zero. Otherwise, an error number shall be returned to indicate the error.
         //如果成功,会保存新的key,失败返回错误码

ERRORS
       The pthread_key_create() function shall fail if:
        //在以下情况会失败

       EAGAIN The system lacked the necessary resources to create another thread-specific data key, or  the  sys-
              tem-imposed limit on the total number of keys per process {PTHREAD_KEYS_MAX} has been exceeded.
                //系统缺少必要的资源去创造key,或者key的数量已经达到了规定的最大值
    
       ENOMEM Insufficient memory exists to create the key.
                    //缺少内存

       The pthread_key_create() function shall not return an error code of [EINTR].
         //不会返回EINTR



 PTHREAD_KEY_DELETE(3P)     POSIX Programmer’s Manual    PTHREAD_KEY_DELETE(3P) PROLOG
       This manual page is part of the POSIX Programmer’s Manual.  The Linux implementation of this interface may
       differ (consult the corresponding Linux manual page for details of Linux behavior), or the  interface  may
       not be implemented on Linux.
         //这只是POSIX的手册,Linux对这个接口的实现可能不一样,或者有的根本没有实现这个接口
        
NAME
       pthread_key_delete - thread-specific data key deletion
        //删除key

SYNOPSIS
       #include
        //头文件

       int pthread_key_delete(pthread_key_t key);

DESCRIPTION
       The  pthread_key_delete()  function  shall  delete  a  thread-specific  data  key  previously  returned by
       pthread_key_create(). The thread-specific data values associated with key need not be  NULL  at  the  time
       pthread_key_delete() is called.  It is the responsibility of the application to free any application stor-
       age or perform any cleanup actions for data structures related to the deleted key  or  associated  thread-
       specific  data  in  any  threads;  this cleanup can be done either before or after pthread_key_delete() is
       called. Any attempt to use key following the call to pthread_key_delete() results in undefined behavior.
         // pthread_key_delete() 会删除key,当pthread_key_delete() 被调用的时候,key关联的值不必为空。当删除key
        //的之后又必要去释放内存空间,做一些清理操作。这些清理操作可以在pthread_key_delete() 之前或之后调用
        //任何试图在pthread_key_delete() 之后使用key的操作都是未知的


       The pthread_key_delete() function shall be callable from within destructor functions. No destructor  func-
       tions shall be invoked by pthread_key_delete(). Any destructor function that may have been associated with
       key shall no longer be called upon thread exit.
        // pthread_key_delete()可以在析构函数中被调用,pthread_key_delete()不能调用析构函数。当线程退出时
        //析构函数不能再被调用


RETURN VALUE
       If successful, the pthread_key_delete() function shall return zero; otherwise, an error  number  shall  be
       returned to indicate the error.
         //成功返回0,失败返回错误码

ERRORS
       The pthread_key_delete() function may fail if:
        //在以下情况会失败

       EINVAL The key value is invalid.
                      //key的值是无效的


PTHREAD_GETSPECIFIC(3P)    POSIX Programmer’s Manual   PTHREAD_GETSPECIFIC(3P)
PROLOG
       This manual page is part of the POSIX Programmer’s Manual.  The Linux implementation of this interface may
       differ (consult the corresponding Linux manual page for details of Linux behavior), or the  interface  may
       not be implemented on Linux.
         //这只是POSIX的手册,Linux对这个接口的实现可能不一样,或者有的根本没有实现这个接口

NAME
       pthread_getspecific, pthread_setspecific - thread-specific data management

SYNOPSIS
       #include
        //头文件

       void *pthread_getspecific(pthread_key_t key);
       int pthread_setspecific(pthread_key_t key, const void *value);

DESCRIPTION
       The  pthread_getspecific()  function shall return the value currently bound to the specified key on behalf
       of the calling thread.
        // pthread_getspecific()返回当前线程绑定的key的值

       The pthread_setspecific() function shall associate a thread-specific value with a key obtained via a  pre-
       vious  call  to  pthread_key_create().  Different threads may bind different values to the same key. These
       values are typically pointers to blocks of dynamically allocated memory that have been reserved for use by
       the calling thread.
        // pthread_setspecific() 会绑定一个值到key,不同的线程可以绑定不同的值到相同的key,这些值都在当前线程
         //的动态申请的内存里

       The  effect  of  calling pthread_getspecific() or pthread_setspecific() with a key value not obtained from
       pthread_key_create() or after key has been deleted with pthread_key_delete() is undefined.
        //当调用 pthread_getspecific() or pthread_setspecific()  时的key不是经过 pthread_key_create()创建的或者已经
         //由  pthread_key_delete()删除了,那么结果是未知的

       Both pthread_getspecific() and pthread_setspecific() may be called from a thread-specific data  destructor
       function.  A  call  to pthread_getspecific() for the thread-specific data key being destroyed shall return
       the value NULL, unless the value is changed (after the destructor starts) by  a  call  to  pthread_setspe-
       cific(). Calling pthread_setspecific() from a thread-specific data destructor routine may result either in
       lost storage (after at least PTHREAD_DESTRUCTOR_ITERATIONS attempts at  destruction)  or  in  an  infinite
       loop.
        //析构函数可以调用 pthread_getspecific() 和 pthread_setspecific()  ,当 pthread_getspecific()的时候如果key已经被
         //销毁,那么获得的值是NULL,除非他由 pthread_setspecific() 改变了。在析构函数中调用  pthread_setspecific()可能
         //导致内存泄露,或者死循环

        Both functions may be implemented as macros.
        //这两个函数可以由宏定义实现

RETURN VALUE
       The  pthread_getspecific()  function shall return the thread-specific data value associated with the given
       key. If no thread-specific data value is associated with key, then the value NULL shall be returned.
        //  pthread_getspecific()会返回一个key的值,如果没有值绑定到key,那么返回null

       If successful, the pthread_setspecific() function shall return zero; otherwise, an error number  shall  be
       returned to indicate the error.
        // pthread_setspecific()如果成功返回0,失败返回错误码

ERRORS
       No errors are returned from pthread_getspecific().
        // pthread_getspecific() 没有错误码

       The pthread_setspecific() function shall fail if:
        // pthread_setspecific()会因为以下情况失败

       ENOMEM Insufficient memory exists to associate the value with the key.
                     //没有内存空间

       The pthread_setspecific() function may fail if:
          //pthread_setspecific()会因为以下情况失败

       EINVAL The key value is invalid.
                    //key的值是无效的

       These functions shall not return an error code of [EINTR].
          //不会返回EINTR

五、实例

点击(此处)折叠或打开

  1. /*DATE:            2015-4-17
  2.  *AUTHOR:        WJ
  3.  *DESCRIPTION:    线程到私有数据,    一个像errno一样到数据
  4.  */
  5. #include "apue.h"

  6. pthread_key_t key;

  7. void *thread_fun1(void *arg)
  8. {
  9.     printf("thread 1 start!\n");
  10.     int a = 1;
  11.     //将a和key关联
  12.     pthread_setspecific(key, (void *)a);
  13.     sleep(2);
  14.     printf("thread 1 key->data is %d\n", pthread_getspecific(key));
  15. }
  16. void *thread_fun2(void *arg)
  17. {
  18.     sleep(1);
  19.     printf("thread 2 start!\n");
  20.     int a = 2;
  21.     //将a和key关联
  22.     pthread_setspecific(key, (void *)a);
  23.     printf("thread 2 key->data is %d\n", pthread_getspecific(key));
  24. }

  25. int main()
  26. {
  27.     pthread_t tid1, tid2;
  28.     
  29.     //创造一个key
  30.     pthread_key_create(&key, NULL);

  31.     //创造新线程
  32.     if(pthread_create(&tid1, NULL, thread_fun1, NULL))
  33.     {
  34.         printf("create new thread 1 failed\n");
  35.         return;
  36.     }
  37.     if(pthread_create(&tid2, NULL, thread_fun2, NULL))
  38.     {
  39.         printf("create new thread 2 failed\n");
  40.         return;
  41.     }

  42.     //等待新线程结束
  43.     pthread_join(tid1, NULL);
  44.     pthread_join(tid2, NULL);

  45.     pthread_key_delete(key);

  46.     return;
  47. }


作业:将一块动态申请的内存关联到key,访问pthread_getspecific(key),请注意内存的销毁

相关文章
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
51 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
23 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
20 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
34 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
37 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
41 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
28 1
|
2月前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
50 2
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
62 6
|
2月前
|
存储 运维 NoSQL
Redis为什么最开始被设计成单线程而不是多线程
总之,Redis采用单线程设计是基于对系统特性的深刻洞察和权衡的结果。这种设计不仅保持了Redis的高性能,还确保了其代码的简洁性、可维护性以及部署的便捷性,使之成为众多应用场景下的首选数据存储解决方案。
42 1