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()函数 等待它创建的线程运行结束;
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函数,会导致线程所在的进程退出。