前言
操作系统实际上并没有向上提供用于创建线程的接口,因此在上层就通过封装实现了线程控制对应所需的库函数,实现对线程的操作。
一、创建
创建线程的库函数:
int pthread_create(pthread_t *tid, pthread_attr_t *attr, void*(*routine)(void*), void *arg);
- tid:获取线程id,也是线程的操作句柄
- attr:设置线程属性,通常设置为NULL
- routine:函数指针,线程入口函数,线程会去运行这个函数,运行完后线程退出
- arg:传递给线程入口函数的参数
- 返回值:成功返回0;失败返回非0,就是错误编号。但不是errno,errno是系统调用接口的错误编号,而pthread_create是库函数
- 头文件:
#include<pthread.h>
- 创建的线程中,main函数是主线程,其他线程都是普通线程;创建的线程哪个先运行不一定,主要看cpu调度
- 线程的有关操作都是库函数,所以在编译链接时,要链接pthread库
创建线程代码:
当我们运行一个程序时,都会有主线程,而主线程就是main函数,我们在主线程中创建一个普通线程,让普通线程完成指定功能,主线程不能先于普通线程退出。
我们都知道进程会有pid,一个进程中有一个或者多个线程,那么进程的这个pid是哪个线程的pid?
ps aux -L
查看轻量级进程
linux下的进程其实就是一个线程组。
在linux下,进程就是一个task_struct结构体,在这个结构体中有两个成员
struct task_struct{
pid_t pid;
pid_t tgid;//线程组id
}
task_struct->pid----轻量级进程(LWP)id
task_struct->tgid(线程组id)–所有轻量级进程拥有相同的线程组id 等于 主线程pid
什么是tid?
tid是线程id,是一个无符号的长整型数据,也是线程的操作句柄。每个线程被创建出来后,都会开辟一块空间,存储线程相关的信息,而tid就是存储线程信息的首地址。
所以查看tid时,要通过%p
格式来查看。
pthread_t pthread_self(void)
:返回调用线程的tid,谁调用返回谁的
二、退出
- 在线程入口函数中
return
,线程入口函数运行完之后,线程就会自动退出。main函数return
退出的是进程。
在普通线程中调用return,线程运行完之后,自动退出,不影响其他线程
主线程return退出的是进程,释放进程所有资源,所有线程退出
void pthread_exit(void *retval)
----退出调用线程,谁调用退出谁。retval是线程退出返回值。
主线程调用pthread_exit
并不会使进程退出,只能使主线程退出,不影响其他线程,直至所有的线程退出,进程才会退出。
普通线程调用
pthread_exit
库函数,普通线程退出
int pthread_cancel(pthread_t tid)
----退出指定线程,线程是被动退出的
三、等待
等待 :等待指定的线程退出,获取退出线程返回值,回收资源。
一个线程被创建后,默认的情况下,退出时也不会自动释放资源。
每个线程都有一个属性(分离属性),默认是joinable状态,处于这个状态的线程退出后不会自动释放资源,退出后需要被其他线程等待获取其返回值并释放资源。
默认情况下,线程必须被等待;如果不等待,则造成资源泄漏
int pthread_join(pthread_t tid, void **retval);
---- 等待指定的线程退出,获取返回值,这是一个阻塞函数,如果线程没有退出则一直等待。
- tid:指定要等待的线程
- retval:用于获取线程返回值
线程退出返回值是字符指针,不能是字符串首地址。因为字符串常量放在文字常量区,程序结束后由系统释放,指针retval指向字符串常量所在的地址。但是retval数组是在栈区开辟空间,并把字符串常量复制过去,retval数组指向的是在栈区存放字符串常量的位置,函数结束后,栈上的空间自动归还给系统,那么retval数组就成为野指针,就不能正确得到线程的返回值
注意:线程之间通过参数或者返回值传递数据极度需要注意数据生命周期,局部数据出了作用域空间就会被释放。
四、分离
分离:将线程的分离属性设置为detach状态(将joinable状态改为detach状态)
处于joinable状态的线程退出后,需要被其他线程等待获取其返回值并释放资源。
处于detach状态的线程退出之后,自动释放资源,不需要被等待。
使用场景:对线程的返回值不关心,也不想等待线程退出,就可以使用分离。
int pthread_detach(pthread_t tid);
---- 将指定线程分离属性设置为detach状态
线程分离不一定需要其他线程设置,每个线程可以将自己进行线程分离,也就是在函数中调用pthread_detach
函数,可以通过pthread_self
获取线程本身的tid。