linux应用软件编程:线程
一、线程的概念
1.定义
线程是由某个进程的创建。<线程是操作系统任务分配的最小单位。>
2.线程的创建
进程创建线程时,会为其分配独立的“栈区空间(默认8M)”。线程和所在进程,以及进程中的其他线程,共用进程的堆区、数据区、文本去。
3.线程的调度
宏观并行,微观串行。
4.线程的消亡
线程的退出
回收线程的资源空间
5.进程和线程的区别
进程 | 线程 | |
定义 | 进程是操作系统资源分配的最小单位 | 线程是操作系统任务调度的最小单位 |
资源消耗 | 资源消耗大,每次创建 | 资源开销小,只需要所在进程为其开辟8M的栈区空间 |
效率角度 | 由操作系统所创建,创建时耗时比线程大,跨进程调度比跨线程调度慢 | 由所在进程创建 |
通信角度 | 进程间不能直接通信,需要使用进程间通信机制(IPC机制) | 通信简单,可以使用线程共享的区域进行通信(比如全局变量) |
安全性角度 | 进程安全性比线程高,各进程空间独立 | 线程没有进程安全性好,一个线程异常可能影响同一进程中的所有线程。 |
二、实现线程间编程
(一)线程编程的步骤
1.线程的创建
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:创建一个新的线程
参数:
thread : 保存线程ID的变量地址
attr:线程属性的对象地址
NULL : 按照默认属性创建
start_routine:函数的指针:指向线程启动后要执行的任务(线程任务函数)
arg:为线程任务函数传递的参数
返回值:
成功:0
失败:非0
2.线程的调度
由操作系统调度
3.线程消亡:
1. 线程退出:
1)在线程任务函数中使用return结束线程
2)pthread_exit(NULL)退出线程;
2. 线程回收:pthread_join(tid, NULL);
- void pthread_exit(void *retval);
功能:退出一个线程任务
参数:
retval:向回收的线程传递的参数的地址
NULL: 表示不传递参数
- int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收线程资源空间
参数:
thread:要回收的线程ID
retval:用来保存线程退出时传递的参数
NULL: 不接收传递的参数
返回值:
成功:0
失败:-1
(二)线程回收的策略
1. 分离属性的线程:不需要回收,由操作系统回收。(没有空闲的线程可帮忙回收时)
2. 非分离属性的线程:pthread_join()阻塞回收
- 线程属性:
1. 分离属性:不需要被其他线程回收的线程称为分离属性得到线程,将来会被操作系统所回收
int pthread_detach(pthread_t thread);
功能:将线程设置成分离属性的线程
2. 非分离属性:可以被其他线程回收或者结束的线程,称为非分离属性的线程
(默认属性:非分离属性)
三、线程间通信——使用全局变量通信
临界资源:多个线程可以同时访问的资源称为临界资源:比如,全局变量、共享内存区域等
多个线程在访问临界资源时,存在资源竞争问题。
1.解决资源竞争问题——采用互斥机制
互斥机制:多个线程访问临界资源时,具有排他性访问的机制(一次只允许一个线程对该临界资源进行访问)。
2.实现步骤
1. 创建互斥锁:pthread_mutex_t
2. 初始化互斥锁:pthread_mutex_init();
- int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
功能:初始化互斥锁
参数:
mutex:锁对象地址
attr:锁的属性 (NULL:默认属性)
返回值:
成功:0
失败:-1
3. 加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);
4. 解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);
5. 销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex);
3.实现过程及示例
四、线程间的同步通信
(一)概念
1.定义:
线程间同步机制:让多个线程在执行某个任务时,具有先后顺序的执行。
2.实现途径
通过信号量,实现线程间同步。
(二)操作步骤
1. 定义信号量对象 :sem_t
2. 初始化信号量 : sem_init();
- int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号量
参数:
sem:要初始化的信号量对象地址
pshared:
0 : 线程间共享
非0 : 进程间共享
value:信号量的初始值(初始资源数)
返回值:
成功:0
失败:-1
3.申请信号量: P操作 :int sem_wait(sem_t *sem);
释放信号量:V操作:int sem_post(sem_t *sem);
PV操作
4. 销毁信号量:int sem_destroy(sem_t *sem);
(三)实现过程
(四)具体示例程序
创建三个线程,分别让这三个线程按照顺序打印:A-->B-->C-->A-->B-->C
五、锁进程(互斥锁、信号量、读写锁、自旋锁)
1.死锁定义
死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。
2.产生的原因
- 忘记释放锁
- 重复加锁
- 多线程多锁,抢占锁资源不当
3.产生死锁的四个必要条件
一个资源每次只能被一个进程使用(一个执行流获取锁后,其它执行流不能再获取该锁)。
一个进程因请求资源而阻塞时,对已获得的资源保持不放(执行流本身使用着一把锁并不释放,还在请求别的锁)。
进程已获得的资源,在末使用完之前,不能强行剥夺(A执行流拿着锁,其它执行流不能释放)。
- 互斥条件
- 请求与保持条件
- 不剥夺条件
- 循环等待条件
若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。
4.解决方法
- 锁一定要成对出现
- 使线程的加锁顺序一致
- 破坏环路等待条件
使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的锁
pthread_mutex_trylock();
int sem_trywait(sem_t *sem);
六、进程间通信——IPC机制
1.采用IPC机制通信的原因
进程空间独立,无法直接通信,需要IPC机制实现。
2.同一主机进程间的通信
无名管道、有名管道、信号(进程间通信)
共享内存(效率最高)、消息队列、信号量集(信号灯)
- 古老的通信方式
- IPC对象通信
- 主要应用在不同主机的通信方式
socket通信:网络通信