进程与线程(线程)
线程
1.基本概念:
- 线程是一个轻量级的进程
- 线程本质就是一个进程
- 线程和进程不完全一致,轻量指空间,进程空间和线程空间管理方法不同
2.进程和线程区别:
1. 线程本质
是进程,线程是任务创建、调度、回收的过程
2. 进程空间:
文本段 + 数据段 + 系统数据段
3. 线程空间:
- 线程必须位于进程空间内部,没有进程,线程无法独立存在
- 一个进程中的所有线程共享文本段+数据段+堆区,独享栈区
- 线程独享的栈区默认为8M
- 一个进程中的多个线程切换调度任务时,资源开销比较小
4. 进程和线程区别:
- 线程是CPU任务调度的最小单元
- 进程是操作系统资源分配的最小单元
3.多进程和多线程的优缺点
1. 多线程和多进程对比:
多线程 vs 多进程对比(C语言)
特性 | 多线程(Thread) | 多进程(Process) |
---|---|---|
创建方式 | 使用 pthread_create (POSIX线程) | 使用 fork 或 exec 系列函数 |
资源开销 | 轻量级,共享同一进程的内存空间 | 重量级,独立内存空间,复制父进程资源(写时复制) |
通信机制 | 共享全局变量、互斥锁(pthread_mutex_t )、条件变量 | 管道(pipe )、共享内存、信号、套接字、消息队列 |
同步机制 | 互斥锁、信号量、读写锁 | 文件锁、信号量、信号 |
数据隔离性 | 线程共享数据,需同步避免竞争 | 进程数据默认隔离,共享需显式操作 |
崩溃影响 | 线程崩溃可能导致整个进程终止 | 单个进程崩溃不影响其他进程 |
上下文切换成本 | 低(共享地址空间) | 高(需切换内存映射表) |
适用场景 | CPU密集型任务(需并行计算)、高响应需求 | 需要高稳定性、隔离性的任务(如服务隔离) |
示例代码 | ```c | ```c |
#include <pthread.h> | #include <unistd.h> | |
void* task(void* arg) { /.../ } | int main() { |
跨平台兼容性
多进程(如 fork
)在 Unix/Linux 系统支持良好,Windows 需使用 CreateProcess
;多线程的 pthread
需注意平台实现差异。
4.线程的调度:
- 调度保持一致
- 宏观并行,微观串行
5.线程的消亡:
线程结束需要回收线程空间,否则产生僵尸线程
6.线程的函数接口
1. 函数接口:
2. pthread_create
3. pthread_self
4. pthread_exit
5. pthread_join
注意:
tid对应的线程只要不退出,pthread_join阻塞等待结束回收线程空间 pthread_join具备同步功能
7.线程传参
1. pthread_create
可以通过pthread_create第四个参数实现对线程内部的传参
#include "../head.h"/* 线程参数类型 */typedef struct pthread_arg{pthread_t tid;
char threadname[32];
int sleeptime;
}pthread_arg_t;//存放线程ID//线程名称
//睡眠时间
void *thread(void *arg){pthread_arg_t *parg = arg;printf("%s(TID:%#lx)开始执行\n", parg->threadname, parg->tid);while (1){printf("%s正在执行\n", parg->threadname);sleep(parg->sleeptime);}return NULL;}int main(void){
int i = 0;pthread_arg_t args[4] = {{0, "采集线程", 1},{0, "存储线程", 2},{0, "显示线程", 5},{0, "日志线程", 10},};for (i = 0; i < 4; i++){pthread_create(&args[i].tid, NULL, thread, &args[i]);}for (i = 0; i < 4; i++){pthread_join(args[i].tid, NULL);}return 0;}
8.线程属性
1. 线程属性:
加入属性:
线程结束需要pthread_join手动回收
分离属性:
线程结束后系统自动回收线程空间
2. 函数接口:
1. pthread_attr_init
2.pthread_attr_setdetachstate
3. pthread_attr_destroy
3. 区别:
1. 分离属性:
- 线程结束后,操作系统自动回收空间
- 不需要手动调用pthread_join回收线程空间
2. 加入属性:
- 可以回收到线程结束的状态
- 可以完成线程间的同步
3.线程间通信:
1.概念:
多个线程间传递信息
2.方式:
采用全局变量
原因:
- 进程是操作系统资源分配的最小单元
- 每个进程空间独立的,包含文本段+数据段(全局变量)+系统数据段
- 一个进程中的多个线程独享栈空间,文本段、数据段、堆区进程多线程共享
注意:
多线程同时操作共享空间会引发资源竞争,需要加上互斥锁解决资源竞争问题
3.互斥锁:
1. 概念
- 解决资源竞争的一种方式,可以看成是一种资源。
- 只能加锁一次,加锁期间不能再次加锁,也不能强制占有一个已经加锁的锁资源,必须等待锁 资源释放,也就是解锁后才能继续操作该锁
- 加锁和解锁中间的代码称为临界代码,也称为临界区
- 只能防止多个线程对资源的竞争,不能决定代码的先后执行顺序
- 原子操作:CPU执行原子操作时无法切换调度任务
2. 使用方式:
- 1. 定义互斥锁(全局变量)
- 2. 对锁初始化
- 3. 操作全局资源前先加锁
- 4. 如果加锁成功则完成对全局资源操作
- 5. 如果加锁失败则表示有人占用资源,必须等待其余人释放锁资源才能加锁成功
- 6. 直到加锁成功使用该全局资源
3. 函数接口:
1. pthread_mutex_init
2. pthread_mutex_lock
3. pthread_mutex_unlock
4. pthread_mutex_destroy
4.死锁
1. 概念:
多线程由于加锁解锁错误导致程序无法继续向下运行的状态称为死锁状态,简称为死锁
2. 死锁产生的四个必要条件:
- 1. 互斥条件
- 2. 不可剥夺条件
- 3. 请求保持条件
- 4. 循环等待条件
3. 如何避免死锁:
- 1. 加锁顺序保持一致
- 2. 使用pthread_mutex_trylock替换pthread_mutex_lock
5.信号量
1. 概念:
- 信号量是一种资源
- 信号量只能完成四种操作:初始化、销毁、申请、释放
- 如果信号量资源数为0,申请资源会阻塞等待,直到占用资源的任务释放资源,资源数不为0时 才能申请到资源并继续向下执行
- 释放资源不会阻塞
2. 函数接口:
1. sem_init
2. sem_destroy
3. sem_wait
注意:
- 申请信号量会让信号量资源数-1
- 如果信号量资源数为0,则会阻塞等待,直到有任务释放资源,才能拿到资源并继续向下执行