基础知识
本质上是进程,被称为轻量级进程,使用
ps -Lf 进程号来查询进程中的线程,每个进程至少一个线程(用于调度)
- 进程是分配资源的单位,线程是调度的基本单位,同一个进程之间的线程共享堆内存、全局变量(函数) 和 文件描述符,另外还有信号处理函数
- task_struct中有如下结构
pid_t pid; //此其实是**LWP/TID** pid_t tgid; //此才是os原理中的pid,线程组编号getpid()返回的是tgid,gettid()返回的才是pid,使用ps获得的PID都是tgid - 主线程pid和tgid相等,创建的线程也有PCB,且其中的tgid等于主线程的tgid,pid为内核分配,可以使用
gettid()或ps -Lf获得; - 线程可以看作寄存器和栈的集合
- 线程对于信号是争夺的,谁先获得信号就谁处理,但是对于信号阻塞集,每个线程是可以单独设置的;不推荐在线程中使用信号
- 线程中如果创建进程,除非立马使用
exec(),在子进程中其余线程会直接终止 - 如果线程中调用
exit(),会退出整个进程 - 主线程如果直接使用return退出,则整个进程都终止,导致线程全部关闭,正确做法是使用
pthread_exit()退出主线程; - 线程分为可连接和已分离两种状态,可连接线程退出后,需要手动释放资源pthread_join(),而进程退出后,会自动释放资源,并保留PCB父进程读取退出状态后释放;如果是已分离线程退出,则会自动释放资源
操作函数
#include <pthread.h>
pthread_self()
- 对应进程的getpid(),注意返回的是pthread_t类型,unsigned long类型,输出使用%lu;
- 返回的 ID 由线程库(如 glibc)维护,仅在当前进程内唯一,LWP是全局唯一的,两者不同;
pthread_create()
对应进程的fork()
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
参数解析
-
thread:传出参数,存储新线程的 ID(pthread_t类型) ,与fork()返回方式不同 -
attr:线程属性(如栈大小、调度策略),通常设为NULL使用默认属性 -
start_routine:线程入口函数,格式为void* func(void*) -
arg:传递给入口函数的参数,可传递值或结构体地址,注意类型是void *,如果同时创建多个线程,如果传递地址则会导致数据共享
返回值:成功返回0,失败返回错误码
pthread_exit()
类似于exit()
void pthread_exit(void *retval);
参数解析
retval:线程退出状态,可由pthread_join()获取。若无需返回值,设为NULL,注意状态返回的是指针,故主线程中也需要使用指针变量来接受
pthread_join()
等价于wait()
int pthread_join(pthread_t thread,void **retval);
参数解析
thread:目标线程的 ID。retval:接收线程退出状态的指针。若不需要状态,设为NULL,注意是指针的指针,因为需要接受指针变量
注意事项
-
僵尸线程:未回收的线程会占用资源,必须调用
pthread_join()或设置分离属性 -
错误处理:
ESRCH:目标线程不存在。EINVAL:目标线程已分离。EDEADLK:死锁(如等待自身)
pthread_cancel()
类似于kill(),但并不是发送系统信号,向目标线程发送取消请求,目标进程是否取消要根据设置来决定;取消后也需要使用pthread_join()来回收PCB
int pthread_cancel(pthread_t thread);
-
取消状态(由
pthread_setcancelstate控制):PTHREAD_CANCEL_ENABLE(默认):线程接受取消请求。PTHREAD_CANCEL_DISABLE:忽略取消请求。
-
取消类型(由
pthread_setcanceltype控制):- 延迟取消(
PTHREAD_CANCEL_DEFERRED,默认):线程需运行到 取消点(如系统调用或显式调用pthread_testcancel())才会响应请求。 - 异步取消(
PTHREAD_CANCEL_ASYNCHRONOUS):线程可在任意指令位置被终止,但可能导致资源泄漏
- 延迟取消(
pthread_detach()
将线程设置为分离状态,使其终止后自动释放资源,不需要再调用
pthread_join()
int pthread_detach(pthread_t thread);
注意事项
- 主线程不要使用return退出,而是使用pthread_exit();
- 避免僵尸线程:
- pthread_join(但是其实Linux会自动回收,不掉也不会产生僵尸线程,只是为了方便迁移程序才需要调用)
- pthread_detach
- pthread_create指定分离属性:
pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); pthread_create(&tid,&attr,tfn,NULL);
3.malloc和mmap申请的内存可以被其他线程释放;
锁
内容太多且多进程也涉及,故放置于下一章节