Linux多线程

80 阅读4分钟

基础知识

本质上是进程,被称为轻量级进程,使用ps -Lf 进程号来查询进程中的线程,每个进程至少一个线程(用于调度)

  1. 进程是分配资源的单位,线程是调度的基本单位,同一个进程之间的线程共享堆内存全局变量(函数)文件描述符,另外还有信号处理函数
  2. task_struct中有如下结构
    pid_t pid;  //此其实是**LWP/TID**
    pid_t tgid; //此才是os原理中的pid,线程组编号
    
    getpid()返回的是tgid,gettid()返回的才是pid,使用ps获得的PID都是tgid
  3. 主线程pid和tgid相等,创建的线程也有PCB,且其中的tgid等于主线程的tgid,pid为内核分配,可以使用gettid()ps -Lf获得;
  4. 线程可以看作寄存器和栈的集合
  5. 线程对于信号是争夺的,谁先获得信号就谁处理,但是对于信号阻塞集,每个线程是可以单独设置的;不推荐在线程中使用信号
  6. 线程中如果创建进程,除非立马使用exec(),在子进程中其余线程会直接终止
  7. 如果线程中调用exit(),会退出整个进程
  8. 主线程如果直接使用return退出,则整个进程都终止,导致线程全部关闭,正确做法是使用pthread_exit()退出主线程;
  9. 线程分为可连接已分离两种状态,可连接线程退出后,需要手动释放资源pthread_join(),而进程退出后,会自动释放资源,并保留PCB父进程读取退出状态后释放;如果是已分离线程退出,则会自动释放资源

操作函数

#include <pthread.h>

pthread_self()

  1. 对应进程的getpid(),注意返回的是pthread_t类型,unsigned long类型,输出使用%lu;
  2. 返回的 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);

注意事项

  1. 主线程不要使用return退出,而是使用pthread_exit();
  2. 避免僵尸线程:
  • 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申请的内存可以被其他线程释放;

内容太多且多进程也涉及,故放置于下一章节