面试题答案
一键面试死锁产生原因分析
- 资源竞争:在非阻塞I/O网络编程高并发场景下,多个线程或进程可能同时竞争有限的网络资源,如套接字描述符、缓冲区等。例如,线程A持有资源R1并请求资源R2,而线程B持有资源R2并请求资源R1,此时若双方都不释放已持有的资源,就会产生死锁。
- 锁顺序混乱:不同线程获取锁的顺序不一致。假设线程1按照顺序获取锁L1、L2,而线程2按照顺序获取锁L2、L1。当线程1获取了L1,线程2获取了L2时,就会出现死锁。在非阻塞I/O中,由于异步操作的特性,更容易出现这种锁顺序混乱的情况。
- 异步操作导致的依赖循环:非阻塞I/O的异步特性使得操作之间的依赖关系变得复杂。例如,一个异步读操作依赖于另一个异步写操作的完成,而写操作又依赖于读操作的某种状态,若处理不当,就会形成依赖循环,进而导致死锁。
预防死锁方案
- 资源分配算法:
- 银行家算法:该算法需要预先知道系统中所有进程对各类资源的最大需求量。系统在每次资源分配前,会检查此次分配是否会使系统进入不安全状态。如果不会,则进行分配,否则拒绝分配。在非阻塞I/O场景下,可将网络资源(如套接字、缓冲区等)视为银行家算法中的资源类型。例如,假设有三个进程P1、P2、P3,三种资源R1、R2、R3,进程P1对资源R1最大需求为3,当前已分配1;进程P2对资源R2最大需求为4,当前已分配2等。每次有新的资源请求时,系统按照银行家算法检查是否会导致不安全状态。
- 资源分配图算法:通过资源分配图(有向图,顶点包括进程和资源,边表示进程请求资源或资源分配给进程)来检测死锁。若资源分配图中存在环,且环中的每个资源类型只有一个实例,那么系统处于死锁状态。预防死锁时,每次资源分配操作后,都检查资源分配图是否会形成环。若会形成环,则拒绝分配。
- 调度策略:
- 优先级调度:为每个线程或进程分配一个优先级。在请求资源时,高优先级的任务优先获取资源。例如,对于处理关键业务逻辑的线程赋予较高优先级,对于一些辅助性的线程赋予较低优先级。在非阻塞I/O场景中,对于处理实时数据传输的线程可设置高优先级,优先获取网络资源。同时,为防止低优先级任务饥饿,可采用时间片轮转与优先级调度相结合的方式,定期提升低优先级任务的优先级。
- 有序资源分配法:为所有资源类型分配一个唯一的序号,线程必须按照序号从小到大的顺序请求资源。例如,若有套接字资源序号为1,缓冲区资源序号为2,那么所有线程都必须先请求套接字资源,再请求缓冲区资源。这样可以避免因锁顺序混乱导致的死锁。
性能优化
- 资源分配算法优化:
- 对于银行家算法,在每次检查安全性时,可采用更高效的数据结构和算法来减少计算量。例如,使用哈希表来快速查找进程对资源的需求和分配情况,而不是通过遍历整个进程和资源列表。同时,可以根据实际场景对银行家算法进行简化,在保证不产生死锁的前提下,降低算法复杂度。
- 资源分配图算法中,可采用更高效的图检测算法,如深度优先搜索(DFS)的优化版本,减少检测环的时间复杂度。例如,在构建资源分配图时,记录每个顶点的访问状态,避免重复访问,提高检测效率。
- 调度策略优化:
- 优先级调度中,动态调整优先级时,可根据任务的实际执行情况和资源需求情况进行更细粒度的调整。例如,对于已经等待了较长时间的低优先级任务,可根据等待时间按比例提升优先级,而不是固定周期提升。这样可以在保证高优先级任务优先执行的同时,减少低优先级任务的饥饿时间。
- 有序资源分配法中,合理安排资源序号,尽量使资源请求顺序符合业务逻辑的执行顺序,减少不必要的资源等待时间。例如,对于网络编程中先进行连接建立(对应套接字资源),再进行数据传输(对应缓冲区资源)的逻辑,将套接字资源序号设置在缓冲区资源序号之前,提高资源分配的效率。