POSIX线程数据类型:

  pthread_t  线程标识符;

  pthread_mutex_t   互斥量;

  pthread_codet_t   条件变量;

  pthread_key_t     线程私有权握访问键

  pthread_attr_t    线程属性对象

  pthread_mutexattr_t  互斥量属性对象

  phtread_condattr_t    条件变量属性对象;

  pthread_once_t    一次性初始化 控制变量 

线程中的基本概念:

  程序中使用线程标识符ID来表示线程。线程ID属于封装的pthread_t类型;

1.创建线程 

  Pthread系统中建立线程的主要方式是调用pthread_create. 如果进程的信号通知机制设为:SIGVE_THREAD,  当线程收到一个POSIX信号时也会创建一个线程。系统中可能还存在其他创建线程的方式

    pthread_t pthread_id;

    int status = pthread_create(&pthread_id, NULL,  thread_func,  NULL);

    第三个参数:函数指针;

    第四个参数:函数的参数指针;

  关于thread_func(线程调用函数):   

    线程函数必须是如下形式,即返回类型为:void* , 参数类型为:void* ;   

    typedef void* (*func)(void* );  //不能写成typedef  void*  (*func)(void* i);

    传、取参数的时候特别注意,传的时候要先转为void*,  取参数的的时候要转回原有的类型;(切记、切记)

 

2. 线程的启动(一般线程函数启动之后就被阻塞了,直到唤醒)

  一旦线程来创建,最终它将开始执行机器指令。 初始指令序列将 带来 在pthread_create调用 中指定的 线程启动函数的执行。

线程启动函数 的运行参数 也是在创建线程时指定的,-----第四个参数;

  在初始线程中, 线程的启动函数(即main函数) 是从程序外部被调用的。 (系统中有文件负责 初始化进程, 然后调用main函数)

 初始化线程 与 普通线程 之间存在一些的不同。

  a. main函数的调用参数( argc  and  argv )与普通线程的启动函数不同( void * 参数 )。

    b.如果普通线程从启动函数中返回,则线程终止而其他线程依然可以运行;

    但初始化进程从main函数中返回时,进程终止(进程内所有线程也被 终止)。

    如果希望在初始化线程终止时,进程中的其他线程继续执行,则需要 在初始化线程中调用pthread_exit而不是从main函数返回;

   c.在大多数系统中, 初始线程运行 在默认进程堆栈上; 该堆栈可以增长到足够的大小; 而在某些实现中,普通的堆栈空间是受限的,  如果线程堆栈溢出,则程序会因为段错误或者总线错误而失败;

3. 运行和阻塞

  线程之所以会睡眠是因为 它需要的某个资源不可用(即被阻塞), 或者因为 系统将处理器分配给其他线程(即被抢占)

  线程大多数时间处于其生命周期中的三个状态:  就绪, 运行 和阻塞;

  线程在以下情况被阻塞:

    1.试图加锁一个已经被锁住的互斥量;

    2.等待某个条件变量;

    3.调用Singwait等待尚未发生的信号;

    4.执行无法立即完成的I/O操作

    5.线程还有由于内存页错误之类的系统操作而被阻塞;

    如果新线程此时没有运行, 则新线程将在主线程被阻塞后, 从就绪态 进入运行态。

    当新线程运行完毕并返回时,主线程才会被解除阻塞,返回就绪态。

      主线程 或者立即执行 或者 等到创建的线程 终止 后 重新运行直到结束;

   调用pthread_join()函数 等待它创建的线程运行结束;  

      pthread_join使一个线程等待另一个线程结束。代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
     所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
 
    一个线程的结束有两种途径,
      一种是象我们下面的例子一样,函数结束了,调用它的线程也就结束了;
      另一种方式是通过函数pthread_exit来实现。
  另外需要说明的是,一个线程不能被多个线程等待,也就是说对一个线程只能调用一次pthread_join,否则只有一个能正确返回,其他的将返回ESRCH 错误;  

4.终止

  线程通常从启动函数中返回来终止自己;

  当调用 pthread_exit退出线程或者调用pthread_cancel 取消线程时, 线程在调用完每个清理过程中也将进入终止态; 这些清理过程 是由线程通过调用pthread_cleanup_push注册的;而且尚未通过调用 pthread_cleanup_pop删除;

  线程还会有私有“线程特定数据”; 如果线程有非空的私有数据值,则与这些数据相关的 destructor函数将被调用;

  如果线程已经被分离。 则它立刻进入下一个环节--------回收;

  否则,线程处于终止态,它还可以被其他线程调用pthread_join 连接--------僵尸线程;因为即使它们已经死了;但还存在,僵尸线程可能会保存其运行时的大部分资源甚至是所有资源。因此不应该让线程长时间处于这种状态;

  当创建不需连接的线程时,应该使用detachstate属性建立线程 使其自动分离;(还有第二种办法)

  终止后线程至少保留了线程ID(pthread_t数据类型)和 void *返回值;

5.回收

  分离一个正在运行的线程不会对线程带来任何影响,仅仅是通知系统当该线程结束时候,其所属资源可以被回收。

  为了确保终止线程的资源对进程可用,应该在每个线程 结束时分离他们。一个没有被分离的线程终止时会保留其虚拟内存。包括他们的堆栈 和其他系统资源。

  分离线程意味着 通知系统不再 需要此线程,允许系统将分配给它的资源回收;

  如果使用detachstate属性(设为PTHREAD_CREATE_DETACH)建立线程; 或者 调用 pthread_detach分离线程, 则当线程结束时将立刻回收;

    如果终止线程没有被分离,则它将一直处于终止态直到    1、被分离(通过pthread_detach分离) 或者  2、被连接(通过pthread_join)。 

线程一旦被分离就不能再访问它;

  回收将  释放 (所有线程  终止时未释放的系统 和进程资源,包括保存线程返回值的内存空间,堆栈,保存寄存器状态的内存空间等); 其中一些资源可能已在线程终止时被释放,

  但必须记住:线程终止后上述资源就不该被访问了。    如果一个线程将其堆栈空间指针通过共享数据传给另一个线程,则该线程终止后,此数据就是陈旧数据了;

  一旦 线程被回收,线程ID就无效了; 不能 再连接它、取消它或者执行其他任何操作。

  终止线程ID(可能是系统数据结构地址)可能被分给新的线程;使用该ID调用pthread_cancel 可能会取消一个不同的线程,而不是返回一个ESRCH错误;

  终止线程将释放所有系统资源,但必须自己先释放有该线程占有的程序资源,调用malloc 或者 mmap 分配的内存可以在任何时候,有任何线程释放。

   互斥量、条件变量、和信号灯 可以由任何线程销毁。只要他们被解锁 并没有线程等待;  但是,只有互斥量的主人能够解锁它。 如果线程终止时还有加锁的互斥量。则该互斥量就不能被再次使用(因为不会被解锁)

  pthread_exit()用于线程退出,可以指定返回值,以便其他线程通过pthread_join()函数获取该线程的返回值。
    return是函数返回,只有线程函数return,线程才会退出。
  exit是进程退出,如果在线程函数中调用exit,进程中的所有函数都会退出! 

线程的终止

   三种方式:  

    线程从执行函数返回,返回值是线程的退出码

    线程被同一进程的其他线程取消

    调用pthread_exit()函数退出。这里不是调用exit,因为线程调用exit函数,会导致线程所在的进程退出。