注:笔者这些文档多出自APUE这本书,旨在督促自己坚持学习,其中没有给出实际的编码例子,是因为本身快速学习的原则,即“花20%的时间掌握80%的内容”,具体的实战会在后续网络编程中一一体现。个人觉得学习编程最佳的路径是在解决问题中掌握知识点。因此线程这几篇都只会讲述个人理解的基础概念。
线程创建的相关函数:
1. int pthread_create(pthread_t*thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);
返回值:若成功,返回0;否则返回错误编号
Eg.
pthread_t thread_id;
pthread_create(thread_id, &attr,&thread_start, &tinfo[tnum]);
- thread_id---------线程创建成功后用于存放线程ID的位置
- attr--------------线程的属性,若设置为NULL则表示默认
- thread_start------新创建的线程从thread_start函数的地址开始运行
- tinfo[tnum]-------需要向thread_start函数传递的参数
返回值:对比t1和t2两个线程是否相等,若相等返回非0数值;否则,返回0
- 线程ID只有在它所属的进程上下文中才有意义,这点和进程ID是有区别的。
返回值:调用线程的线程ID
线程终止:
线程的终止分为两大类:主动终止,被动终止
主动终止
- 线程函数执行return正常返回,返回值就是线程的退出码;
- 线程调用pthread_exit函数,其参数就是线程的退出码;
被动终止
- 在其他线程中调用pthread_cancel函数;
- 任意线程调用exit家族的函数,这种方式比较极端,会导致整个进程退出;
1. void pthread_exit(void*retval);
Eg.
pthread_exit((void *)2);
- retval-----------线程的返回值,即终止状态
返回值:若成功,返回0,;否则,返回错误编号
- 线程通过调用这个函数来请求(并不等待线程终止,它仅仅是提出请求)取消同一进程中的其他线程。
它的行为等同于pthread_exit(PTHREAD_CANCELED);
3. int pthread_join(pthread_tthread, void **retval);
返回值:若成功,返回0;否则,返回错误编号
调用线程将一直阻塞,直到指定的线程调用pthread_exit,从启动例程中返回或者被取消。
retval------如果线程从启动例程中返回,则retval就包含返回码
retval------如果线程被取消,就由retval指定的单元被设置为PTHREAD_CANCELED
- 在调用pthread_join的时候还需要直到线程的启动状态,如果线程是分离状态启动的那么,pthread_join调用就会失败,返回EINVAL,如果是以非分离状态启动的pthread_join就会把线程置于分离状态,这样就可以回收线程资源。
int pthread_detach(pthread_tthread);
返回值:若成功,返回0;否则,返回错误编号。
线程清理处理程序:
线程可以安排它退出时需要调用的函数,这与进程在退出时可以使用atexit函数注册退出函数类似。一个线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说,他们执行顺序与他们注册时相反。
1. void pthread_cleanup_push(void (*rtn)(void *), void *arg);rtn------清理函数rtn是由pthread_cleanup_push函数调度的,调用时只有一个参数arg;
2. voidpthread_cleanup_pop(intexecute);
execute------参数设置为0时,清理函数不被调用。
无论如何pthread_cleanup_pop都将会删除上次pthread_cleanup_push调用建立的清理处理程序。
注意:pthread_cleanup_push和pthread_cleanup_pop必须成对使用。这个可以做个试验测试一下,注释掉你的pthread_cleanup_pop编译会产生什么结果。
线程和进程之间的相似之处:
进程原语 |
线程原语 |
描述 |
fork |
pthread_create |
创建新的控制流 |
exit |
pthread_exit |
从现有的控制流中退出 |
waitpid |
pthread_join |
从控制流中得到退出状态 |
atexit |
pthread_cleanup_push |
注册退出清理函数 |
getpid |
pthread_self |
获取控制流ID |
abort |
pthread_cancel |
请求控制流的非正常退出 |
线程属性:
在前面创建线程的时候我们调用pthread_create函数中有个参数,
const pthread_attr_t *attr是关于设置线程属性的。
接下来就详细总结一下Linux线程的属性。
pthread_attr_t 这个结构体表示将为创建的线程指定类型,该类型的结构体包含了如下属性:
1. detachstate:线程的分离属性
它表示线程结束的时候,是否回收资源。
- PTHREAD_CREATE_JOINABLE:这种是默认属性,表示在线程结束时不回收资源,需要调用pthread_join函数来回收;
- PTHREAD_CREATE_DETACHED:表示线程结束,直接释放资源;
用于设置和获取detach state的函数如下:
int pthread_attr_setdetachstate(pthread_attr_t *attr,int *detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t*restrict attr, int *detachstet );
2. scope:线程的竞争域
该属性有两种:
- PTHREAD_SCOPE_SYSTEM:表示线程和整个操作系统中的线程进行竞争;
- PTHREAD_SCOPE_PROCESS:表示线程只和当前进程内的线程进行竞争,比如线程调度的时候只考虑当前进程中的线程;
用来设置和获取scope的函数如下:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t*attr, int *scope);
3. inheritscheduler:调度继承性
该属性有两种:
- PTHREAD_INHERIT_SCHED:继承进程的调度策略
- PHTREAD_EXPLICIT_SCHED:不使用继承的调度策略,而是使用自己提供的调度策略。
用来设置和获取inherit scheduler的函数如下:
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_ getinheritsched(const pthread_attr_t*attr, int *inheritsched);
4. schedulingpolicy:调度策略
分两大类:
- 普通调度策略(非实时的调度策略):主要包括SCHED_OTHER、SCHED_BATCH和SCHED_IDLE
- 实时调度策略:主要包括SCHED_FIFO和SCHED_RR
用于设置和获取policy的函数如下:
int pthread_attr_setschedpoilcy (pthread_attr_t *attr, int poilcy);
int pthread_attr_ getschedpoilcy(const pthread_attr_t*attr, int *poilcy);
5. schedulingpriority:线程优先级
使用如下函数来设置和获取:
pthread_attr_setschedparm(pthread_attr_t *attr,const structsched_param *param);
pthread_attr_getschedparm(pthread_attr_t *attr, struct sched_param *param);
6. guardsize:警界缓冲区大小,默认值是PAGE_SIZE(4096KB)
用来设置和获取线程栈末尾的警戒缓冲区大小,如果线程栈运行到了警戒区,就会收到信号。
使用如下函数来设置和获取:
int pthread_attr_getguardsize(constpthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize(pthread_attr_t*attr, size_t guardsize);
7. stackaddress:栈起始地址(栈最低内存地址)
一般来说在X86或者X64处理器上,栈都是往地地址方向的,所以该值一般表示栈的末尾(栈顶)
使用如下两个函数来对其进行设置和获取:
int pthread_attr_getstack(constpthread_attr_t *attr, void **stackaddr, size_t *stacksize);
int pthread_attr_setstack(pthread_attr_t*attr, void *stackaddr, size_t stacksize);
8. stacksize:栈大小
对于栈的大小,可以使用如下函数进行设置和获取:
intpthread_attr_getstacksize(constpthread_attr_t *attr, size_t*stacksize);
intpthread_attr_setstacksize(pthread_attr_t*attr, size_tstacksize);
以上属性目前我最常用的就是detach state(线程的分离属性)。