Linux线程
线程原语
pthread_create
创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
pthread_t *thread 传出参数,返回线程tid
const pthread_attr_t *attr
设置线程属性,例如:线程用户栈大小,线程优先级
void *(*start_routine)
(void *) 线程的处理动作 函数指针void*fun(void*)
void *arg
线程处理动作的参数
pthread_self
获取调用线程tid
#include <pthread.h>
pthread_t pthread_self(void);
思考:pthread_self与tid等价吗?
不等价,应为pthread_self在线程中获取tid(线程当前存在)
tid是主控线程的变量在用户栈, 子线程看不到, 所以是主控线程获取输出的 输出时可能值一致(但可能此线程已经被释放,回收)
如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,这只是一种权宜之计。
pthread_exit
线程退出函数
调用线程退出函数,注意和exit函数的区别,任何线程里exit导致进程退出,其他线程未工作结束,主控线程退出时不能return或exit。
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
#include <pthread.h>
void pthread_exit(void *retval);
void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。
pthread_join
等待线程退出函数
#include
<pthread.h>
int pthread_join(pthread_t thread, void **retval);
pthread_t thread:回收线程的tid
void **retval:接收退出线程传递出的返回值
返回值:成功返回0,失败返回错误号
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。
如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。(即为-1)
如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。
如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。
pthread_cancel
在进程内某个线程可以取消另一个线程。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
被取消的线程,退出值,定义在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。可以在头文件pthread.h中找到它的定义:
#define
PTHREAD_CANCELED ((void *) -1)
实例:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> void *thr_fn1(void *arg) { printf("thread 1 returning\n"); return (void *)1; } void *thr_fn2(void *arg) { printf("thread 2 exiting\n"); pthread_exit((void *)2); } void *thr_fn3(void *arg) { while(1) { printf("thread 3 writing\n"); sleep(1); } } int main(void) { pthread_t tid; void *tret; pthread_create(&tid, NULL, thr_fn1, NULL); pthread_join(tid, &tret); printf("thread 1 exit code %d\n", (int)tret); pthread_create(&tid, NULL, thr_fn2, NULL); pthread_join(tid, &tret); printf("thread 2 exit code %d\n", (int)tret); pthread_create(&tid, NULL, thr_fn3, NULL); sleep(3); pthread_cancel(tid); pthread_join(tid, &tret); printf("thread 3 exit code %d\n", (int)tret); return 0; }
结果:退出码分别是1,2,-1.
对于线程3:如果线程中只有while(1),那么pthread_cancle则不能终止线程。如果加了sleep(1);则可以终止线程。
总结:signal何时终止信号?(中断,异常,系统调用)
cancle何时事件何时处理?(系统调用)
就是说线程没有系统调用则不能使用cancle结束,所以我们可以用pthread_testcancle()来产生一次空调用,该函数的作用就是,产生一次系统调用检测终止事件。
线程终止方式
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。
2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
3.线程可以调用pthread_exit终止自己。
同一进程的线程间,pthread_cancel向另一线程发终止信号。系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程.
pthread_detach
将线程置为分离态
#include <pthread.h>
int
pthread_detach(pthread_t tid);
pthread_t tid:分离线程tid
返回值:成功返回0,失败返回错误号。
一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。同理,如果对一个已经处于join回收状态的线程调用depatch设置分离,也会失败。
线程属性
线程属性初始化
先初始化线程属性,再pthread_create创建线程
#include <pthread.h>
int
pthread_attr_init(pthread_attr_t *attr); //初始化线程属性
int
pthread_attr_destroy(pthread_attr_t *attr); //销毁线程属性所占用的资源
线程分离状态的函数:
#include <pthread.h>
int
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
//设置线程属性,分离or非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); //获取程属性,分离or非分离
pthread_attr_t *attr:被已初始化的线程属性
int *detachstate:可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE(非分离线程)这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。
要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
NPTL
1.察看当前pthread库版本
getconf GNU_LIBPTHREAD_VERSION
2.NPTL实现机制(POSIX),Native POSIX Thread Library
3.使用线程库时gcc指定-lpthread
细节注意
1.主线程退出其他线程不退出,主线程应调用ptrhed_exit
2.避免僵线程
pthread_join
pthread_deatch
pthread_create指定分离属性 被join线程 可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
3.malloc和mmap申请的内存可以被其他线程释放
4.如果线程终止时没有释放加锁的互斥量,则该互斥量不能再被使用
5.应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
6.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制