《Unix环境高级编程》、《Posix多线程程序设计》学习笔记和重点摘要。
一、线程的4种状态
状态
|
含义
|
就绪(Ready)
|
线程能够运行,但在等待可用的处理器。可能刚刚创建启动,或刚刚从阻塞中恢复,或者被其他线程抢占
|
运行(Running)
| 线程正在运行。在多处理器系统中,可能有多个线程处于运行态 |
阻塞(Blocked) |
线程由于等待处理器外的其他条件无法运行,如条件变量的改变、加锁互斥量或I/O操作结束
|
终止(Terminated) |
线程从起始函数中返回,或者调用pthread_exit,或者被取消,终止自己并完成所有资源清理工作。不是被分离,也不是被连接
|
二、线程的创建
需要创建线程的方式有: 1、调用pthread_create
2、进程的信号通知机制设为SIGEV_THREAD
注意:新线程可能在当前线程从phtread_create返回之前就运行了。甚至在pthread_create还没返回,新线程就已经执行完了。
线程和初始进程的一些区别:
1,参数不同
2,函数返回结果不同:初始进程从main函数返回后,进程和进程内的所有线程都将终止;普通线程返回时,不影响进程内的其他线程。
如果你希望在初始进程终止时,进程中的其他线程继续执行,则需要在初始进程中调用pthread_exit,而不是从main函数中返回。
3,大多数系统中,初始线程运行在默认进程堆栈上,该堆栈可以增长到足够的尺寸;而在某些实现中,普通线程的堆栈空间是受限的,如果
线程堆栈溢出,则程序会因段错误或总线错误而失败。
三、运行和阻塞
线程阻塞的原因可能是: 1,试图加锁一个已经被锁住的互斥量
2,等待某个条件变量
3,调用Singwait等待尚未发生的信号
4,执行无法立即完成的I/O操作
5,线程还会由于如内存页错误之类的系统操作而被阻塞
四、终止
单个线程可以通过3种方式退出,在不终止整个进程的情况下停止它的控制流:
1,线程只是从启动例程中返回,返回值是线程的退出码。
2,线程可以被同一进程中的其他线程取消。
3,线程调用pthread_exit
头文件
|
#include <pthread.h>
|
函 数
|
void pthread_exit (void *rval_ptr)
|
头文件
|
#include <pthread.h>
|
函 数
|
int pthread_join (pthread_t thread, void **rval_ptr)
|
就可以恢复。如果线程已经处于分离状态,pthread_join 就会调用失败,返回EINVAL。
头文件
|
#include <pthread.h>
|
函 数
|
int pthread_detach( pthread_t tid)
|
状态下不能使用pthread_join等待线程的终止状态。
头文件
|
#include <pthread.h>
|
函 数
|
int pthread_cancel (pthread_t thread)
|
注意:如果线程只是从当前线程返回或者调用pthread_exit正常终止,rval_ptr将包含返回码。但是
如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED.
如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED.
注意:pthread_create和pthread_exit函数的无类型指针参数能传递的数值可以不止一个,该指针可以传递包含更复杂信息
的结构的地址,但是注意这个结构所使用的内存在调用者完成
调用以后必须保证仍是有效的,否则就会出现非法内存访问。
例如,在调用线程(如main函数)的栈上分配了一个结构体,那么其他的线程在使用这个结构体时,结构体中的内容可能已经
改变了。
又如,线程在自己的栈上分配了一个结构体,然后把它的指针传递给pthread_exit,那么当调用pthread_join的线程试图
再次使用该结构时,这个栈可能已经撤销,这块内存可能另作他用。
解决的办法:可以使用全局结构体,不要放在main中。或者使用malloc函数分配结构体内存。
头文件
|
#include <pthread.h>
|
函 数
|
void pthread_cleanup_push (void (*rtn) (void *), void *arg)
|
函 数
|
void pthread_cleanup_pop (int execute)
|
可以建立多个清理处理程序。处理程序记录在栈中,这也意味着它们的执行顺序和它们的注册顺序是相反的。
当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序是pthread_cleanup_push函数来安排的。
当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序是pthread_cleanup_push函数来安排的。
1,调用pthread_exit时
2,响应取消请求时
3,用非0 execute参数调用pthread_cleanup_pop时(如果execute参数设置为0时,清理函数将不被调用。但无论哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理处理函数)
注意:从线程自然的返回(如return)而终止线程的将不会调用线程清理函数。
进程原语和线程原语的比较
进程原语
|
线程原语
|
描述
|
fork
| pthread_create |
创建新的控制流
|
exit
|
pthread_exit
|
从现有的控制流中退出
|
waitpid
|
pthread_join
|
从控制流中得到退出状态
|
atexit
|
pthread_cleanup_push
| 注册在退出控制流时要调用的函数 |
getpid
|
pthread_self
|
获取所在控制流的ID
|
abort
|
pthread_cancel
|
请求控制流的非正常退出
|
五、回收
线程在终止后都要经过分离并且将资源回收。
回收将释放所有在线程终止时未释放的系统和进程资源,包括保存线程返回值的内存空间、堆栈、保存
寄存器状态的内存空间等。在线程终止后上述资源就不该被访问了。
终止线程将释放所有的系统资源,但你必须释放由该线程占有的程序资源。调用malloc或mmap分配的
内存可以在任何时候、由任何线程释放。互斥量、条件变量和信号灯可以由任何线程销毁,只要它们被解锁
并且没有线程等待。
但是,只有互斥量的主人能够解锁它。如果线程终止时还有加锁的互斥量,则该互斥量就不能被再次使
用,因为不会被解锁。