死锁
死锁:是指多个线程因竞争资源而造成的一种互相等待的僵局。例如A等待B释放线程然后去CPU时间片,但B也在等待A在释放线程,这种情况就会造成死锁的情况。
死锁产生的原因主要是:系统资源竞争,进程运行推进顺序不合适。
死锁产生的四个必要条件:
互斥条件:一个资源一次只能被一个进程使用。
请求与保持条件: 进程已经保持了至少一个资源,又提出了新的资源请求,而该资源已经被其他进程占有。此时请求进程被阻塞,而对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完之前,不能被其他进程强行夺走,即只能由进程自己主动释放。
循环等待条件:若干进程间形成首尾相接循环等待资源的关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而”只要上述条件之一不满足,就不会发生死锁“。
线程同步机制:互斥锁+条件变量+读写锁
- 互斥锁:适用于线程可用的资源只有一个,需要互斥访问的情况
- 条件变量:适用线程之间构成条件等待关系的情况
- 读写锁:提高互斥锁在数据库系统数据访问(大量读,较少写)等应用领域的效
互斥锁简单来说是以排他方式防止共享数据被并发访问。且它只有两种状态0(允许)和1(禁止)。可将其看成是特殊意义的全局变量。
使用互斥锁的方式是1.首先申请互斥锁,若互斥锁处于锁定状态,则线程被阻塞。若是处于解锁状态,则申请到该锁并立即占有该锁,使锁处于锁定状态,其他线程访问该资源时会被排他。2.是被锁定的线程才能释放该互斥锁,其他线程试图释放操作是无效的。
1.pthread_mutex_t lock;定义该互斥锁(全局变量)
2.初始化互斥锁 pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
mutex 要初始化的互斥锁的指针
mutexattr 要初始化的互斥锁的属性;NULL表示使用默认属性
成功返回0 否则返回一个错误编号
阻塞申请互斥锁 pthread_mutex_lock (pthread_mutex_t *mutex);
非阻塞申请互斥锁 pthread_mutex_trylock (pthread_mutex_t *mutex);
线程任务完成后释放互斥锁 pthread_mutex_unlock (pthread_mutex_t *mutex);
不使用后需要销毁互斥锁 pthread_mutex_destroy (pthread_mutex_t *mutex);
静态分配的互斥锁:置为常量PTHREAD_MUTEX_INITIALIZER,属性为NULL,也可以调用pthread_mutex_init函数
动态分配的互斥锁:例如通过调用malloc函数分配的互斥锁,只能调用pthread_mutex_init,且在释放内存前需要调用pthread_mutex_destroy
条件变量
条件变量与互斥锁不同,它是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。条件变量不能单独使用,必须配合互斥锁一起实现对资源的互斥访问。
条件变量分为两部分:条件和变量。条件本身是由互斥量保护的,线程在改变条件状态前先要锁住互斥量。
条件变量使线程睡眠等待某种条件出现。条件变量主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。
条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
pthread_cond_t condition;定义全局变量
1.初始化条件变量 pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict cond_attr);
cond 指向要初始化的条件变量指针
cond_attr 指向属性对象的指针,该属性对象定义要初始化的条件变量的特性;NULL表示使用默认属性;
阻塞等待条件变量 pthread_cond_wait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
cond 指向要等待的条件变量的指针
mutex 指向与条件变量cond关联的互斥锁的指针
在指定的时间内阻塞等待条件变量 pthread_cond_timedwait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
nd 指向要等待的条件变量的指针
mutex 指向与条件变量cond关联的互斥锁的指针
abstime 等待过期时的绝对时间,如果在此时间范围内取到该条件变量函数将返回
使用UTC时钟,即为一个绝对时间,该数据结构声明如下
struct timespec
{
long ts_sec; //秒部分
long ts_nsec; //纳秒部分
}
使用这个结构时,需要指定愿意等待多长时间,时间值是一个绝对数而不是相对数。
例如,如果能等待3分钟,就需要把当前时间加上3分钟再转换到timespec结构,而不是把3分钟转换成timespec结构。
通知等待该条件变量的第一个线程 pthread_cond_signal (pthread_cond_t *cond);
如果cond没有阻塞任何线程,则此函数不起作用
如果cond阻塞了多个线程,则调度策略将确定要取消阻塞的线程
显然在此函数被调用时隐含了释放当前线程占用的信号量的操作
通知等待该条件变量的所有线程 pthread_cond_broadcast;(pthread_cond_t *cond);
如果cond上没有阻塞任何线程,则此函数不起作用
销毁条件变量状态 pthread_cond_destroy(pthread_cond_t *cond);
while(1);
意义:这是一个死循环,代码不再向下执行。
用途:
1. 一般在调试代码时,为了检测一部分代码是否OK,防止后面的代码干扰执行结果,会在观测点加上while(1);
2.有些代码检测到运行错误时,会抛出错误(打印、设置错误码),然后进入while(1);
3.机器需要复位时,停止喂看门狗,进入while(1); 迫使看门狗超时,产生硬件复位
while( 1 ) { 代码 }
意义:这里将会重复执行{}中的代码
用途:
1.单片机在不使用操作系统时,主程序一般都使用这种架构
2.操作系统中的进程,执行任务时,有些也会使用这种架构
3.{}中的代码不停地检测某个条件,当条件符合时,跳出该循环,继续向下执行
4.停留在此,等待中断
应用案例
借鉴并整理:
(35条消息) C语言多线程编程-死锁和线程同步方式介绍(一)_shuaixio的博客-CSDN博客_c线程同步