首先来看下进程和线程的关系,线程是进程中的一个单一控制流,它是操作系统运算调度的最小单位,即线程是执行的最小单位,进程是最小的分配资源单位。在Linux下,有时候我们又称线程为轻量级进程,因为进程和线程它们的底层实现都是由一个叫做clone的函数来实现的。这个clone函数是比较底层的,它可以克隆一切可写的东西,例如:堆,栈,非只读数据段等等。在内核层面来看,进程和线程是一样的,它们共享同一片内存地址空间。
线程与线程之间共享了一些东西,例如:文件描述符表、信号的处理方式、进程地址空间(堆、数据段、代码段)、用户id和组id等资源。
线程之间非共享资源有:线程id、栈(内核栈空间和用户栈空间)、errno变量、信号屏蔽字、调度优先级等。
线程有哪些优缺点呢?
优点:1.提高程序的并发性;2.开销小,不用重新分配内存;3.通信和共享数据方便
缺点:1.线程不稳定;2.线程调试比较困难;3.线程无法使用unix经典事件,例如:信号(也不是说不能用,最好不要混用,要不然很麻烦)
Linux的线程是通过pthread_create函数来创建的,其声明如下:
int pthread_create(
pthread_t *thread, //传出参数,保存新线程的线程id
const pthread_attr_t *attr, //线程属性,一般为NULL
void *(*start_routine)(void *), //线程函数指针
void *arg //线程函数参数
);
该函数执行成功返回0,失败返回错误码,请注意这里失败不是返回-1,因为很多Linux系统函数一般都是失败返回-1,errno被设置。虽然每个线程都有自己的errno,但是pthread库并不使用它,它只是为了兼容其它接口实现的。
当使用该函数来创建线程的时候,需要包含pthread.h头文件,而且在编译的时候我们需要添加-lpthread这一句。
要想获取一个线程的id,可以通过pthread_self函数来获得,其声明如下:
pthread_t pthread_self();
该函数返回线程的id,pthread_t在Linux下被定义成unsigned long类型。
前面一个函数也可以获得线程的id(通过传出参数),那么在线程函数里调用pthread_self函数获得的线程id与通过传出参数返回的id是否相同呢?答案是相同的,但通过传参返回的有可能是无效的,当执行pthread_create函数时,会创建一个线程,若该线程直接运行完退出,此时thread参数里边保存的线程id就是无效的。
进程退出可以使用exit函数,那么线程退出可以使用pthread_exit函数,其声明如下:
void pthread_exit(
void *retval
);
该函数的一个参数是用来传递参数的,这个可以是线程的退出值或者是一个地址,也可以传NULL表示不需要返回任何东西。如果是地址,应该是全局的或是堆上分配的,不能线程内部申请的局部地址。既然这个函数是在线程函数内调用的,那么如何获得这个参数呢?可以通过以下这个函数获取:
int pthread_join(
pthread_t thread, //线程的id
void **value_ptr //接收退出线程传递出的返回值
);
该函数获取指定线程的退出值,并释放指定线程的资源,若指定线程没有退出,则该函数将阻塞,直至线程退出才返回。该函数调用成功返回0,失败返回错误码。指定线程以不同的方式退出,保存的值是不同的:
1.如果指定线程是正常退出返回的,则value_ptr保存的是线程函数的退出值
2.如果指定线程是被另一个线程调用pthread_cancel终止的,则value_ptr保存的是常数PTHREAD_CANCELED
3.如果指定线程自己调用pthread_exit终止的,则value_ptr保存的是pthread_exit内的参数
4.如果我们对指定线程的退出状态不感兴趣,可以传NULL给value_ptr
有时需要在一个线程内终止另外一个线程,此时可以调用pthread_cancel,其声明如下:
int pthread_cancel(
pthread_t thread
);
被终止的线程的退出值是PTHREAD_CANCELED,该值在pthread库中被定义成-1。当线程间调用pthread_cancel的时候,系统并不会马上关闭被取消的线程,而是要等到下次系统调用(用户空间切换)的时候,系统才会去真正的关闭线程。
当一个线程退出后,如果没有没pthread_join,那么会发生什么事呢?此时,该线程的资源没有被回收,变成了僵尸线程。有时候,可能并不关心线程的退出值,又不想去回收它,那么可以在开始的时候将这个线程设置分离态,当线程被设置成分离态后,就不需要再去pthread_join了,线程退出后,系统会自动回收线程资源。至于如何设置线程分离态,可以调用pthread_detach,其声明如下:
int pthread_detach(
pthread_t tid //要设置的线程id
);
当一个线程成分离态后,就不能用pthread_join来进行回收了,若调用pthread_join函数,此时会调用失败。