1. 线程与进程的本质区别
进程是操作系统资源分配的基本单位,而线程是CPU调度的基本单位。每个进程都有独立的地址空间,一个进程崩溃后不会影响其他进程;而线程共享进程的资源,一个线程崩溃可能导致整个进程崩溃。
从资源开销来看,进程创建和销毁的开销较大,需要分配独立的内存空间;线程创建和销毁的开销较小,因为它们共享进程的资源。在通信机制上,进程间通信(IPC)需要通过管道、消息队列、共享内存等机制;而线程可以直接读写进程数据段来进行通信。
实际开发中,选择进程还是线程需要考虑以下因素:如果需要更好的隔离性和稳定性,选择进程;如果需要更高的性能和更低的资源开销,选择线程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线程的生命周期与状态转换
线程的生命周期包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)五种状态。新建状态是线程被创建但尚未启动;就绪状态是线程等待CPU时间片;运行状态是线程正在执行;阻塞状态是线程等待某个条件满足;终止状态是线程执行完毕。
状态转换的典型场景包括:
- 新建→就绪:调用start()方法
- 就绪→运行:获得CPU时间片
- 运行→就绪:时间片用完
- 运行→阻塞:等待I/O操作
- 阻塞→就绪:I/O操作完成
- 运行→终止:线程执行完毕
3. 进程间通信(IPC)的常用方式
进程间通信的主要方式包括:
- 管道(Pipe):单向通信,只能在有亲缘关系的进程间使用
- 命名管道(Named Pipe):可以在无亲缘关系的进程间使用
- 消息队列(Message Queue):通过消息链表实现通信
- 共享内存(Shared Memory):最高效的IPC方式,需要同步机制配合
- 信号量(Semaphore):用于进程间同步
- 套接字(Socket):可用于不同主机间的进程通信
在实际项目中,共享内存+信号量的组合通常能提供最佳性能,但实现复杂度较高。对于简单的通信需求,消息队列是较好的选择。
4. 线程同步与互斥机制
多线程编程中最关键的问题就是解决资源竞争和数据一致性问题。常用的同步机制包括:
4.1 互斥锁(Mutex)
最基本的同步机制,保证同一时间只有一个线程能访问共享资源。使用时需要注意避免死锁,常见的死锁场景包括:
- 多个锁的获取顺序不一致
- 持有锁时调用可能阻塞的操作
- 忘记释放锁
4.2 条件变量(Condition Variable)
用于线程间的条件等待和通知机制。典型使用模式:
python复制with lock:
while not condition:
cond.wait()
# 处理条件满足的情况
4.3 信号量(Semaphore)
控制对共享资源的访问数量。二进制信号量(值为1)可以当作互斥锁使用。
4.4 读写锁(Read-Write Lock)
允许多个读操作同时进行,但写操作需要独占访问。适用于读多写少的场景。
5. 线程池的最佳实践
线程池是管理线程的有效方式,可以避免频繁创建销毁线程的开销。配置线程池时需要考虑以下参数:
- 核心线程数:线程池保持的最小线程数
- 最大线程数:线程池允许的最大线程数
- 空闲线程存活时间:超过核心线程数的线程空闲多久后被回收
- 工作队列:存放待执行任务的队列
- 拒绝策略:当任务无法处理时的策略
对于CPU密集型任务,线程数建议设置为CPU核心数+1;对于I/O密集型任务,可以设置为CPU核心数*2。实际项目中,需要通过性能测试来确定最佳参数。
6. 进程与线程的性能优化
6.1 进程优化
- 使用进程池复用进程
- 合理设置进程优先级
- 避免频繁创建销毁进程
- 使用共享内存减少数据拷贝
6.2 线程优化
- 减少锁的粒度
- 使用无锁数据结构
- 避免虚假共享(False Sharing)
- 合理设置线程栈大小
- 使用线程本地存储(TLS)减少同步开销
在Linux系统中,可以通过perf工具分析进程/线程的性能瓶颈,常见的瓶颈包括:
- 锁竞争
- 缓存失效
- 系统调用过多
- 内存访问模式不佳
7. 常见问题排查
7.1 进程无法结束
可能原因:
- 进程处于D状态(不可中断睡眠)
- 进程在等待子进程退出
- 进程被ptrace跟踪
- 进程处于僵尸状态
解决方法:
- 使用kill -9强制终止
- 检查进程状态(ps aux)
- 使用strace跟踪系统调用
7.2 线程泄漏
表现为线程数持续增长,最终耗尽系统资源。常见原因:
- 线程创建后没有正确退出
- 线程池配置不当
- 第三方库创建线程未管理
排查方法:
- 定期监控线程数
- 使用pstack查看线程堆栈
- 检查线程创建点的退出条件
7.3 死锁问题
典型症状是程序挂起,CPU使用率低。排查步骤:
- 获取所有线程的堆栈信息
- 分析锁的持有和等待关系
- 检查锁的获取顺序是否一致
- 使用工具如gdb附加分析
预防死锁的建议:
- 按照固定顺序获取锁
- 使用锁超时机制
- 避免在持有锁时调用外部代码
- 使用更高级的同步原语
