进程与线程
多线程同步
锁与信号量
互斥与同步的实现和使用,主要的方法有两种:锁和信号量.
锁
可以分为忙等待锁和无忙等待锁;
忙等待锁:
- 也被称作 自旋锁;
- 可由Test-and-Set指令(原子指令)实现;
- 当获取不到锁时,线程会一直while循环.
无等待锁: 没获取到锁时,把当前线程放入到锁的等待队列,然后执行调度程序,把CPU让给其他线程.
信号量
- 互斥:信号量初值为1;
- 同步:信号量初值为0;
生产者-消费者问题
- ⽣产者在⽣成数据后,放在⼀个缓冲区中;
- 消费者从缓冲区取出数据处理;
- 任何时刻,只能有⼀个⽣产者或消费者可以访问缓冲区;
哲学家就餐问题
要解决死锁问题。 对于互斥访问有限的竞争问题(如 I/O 设备)⼀类的建模过程⼗分有⽤。
- 哲学家围在⼀起先思考,思考中途饿了就会想进餐;
- 哲学家要拿到左右两边的叉⼦才进餐;
- 吃完后,会把两⽀叉⼦放回原处,继续思考;
方案一: 让偶数编号的哲学家「先拿左边的叉⼦后拿右边的叉⼦」,奇数编号的哲学家「先拿右边的叉⼦后拿左边的叉⼦」。
读者-写者问题
为数据库的访问建立了模型.
- 读-读允许:同⼀时刻,允许多个读者同时读
- 读-写互斥:没有写者时读者才能读,没有读者时写者才能写
- 写-写互斥:没有其他写者时,写者才能写
方案:
死锁
死锁只有同时满足以下四个条件才会发生:
互斥
持有并等待
不可剥夺
环路等待
避免死锁问题的发生
只需要破坏其中一个条件就可以。
使用资源有序分配法: 破坏环路等待条件。
使线程A和线程B总是以相同的顺序申请⾃⼰想要的资源。
悲观锁和乐观锁
互斥锁: 加锁失败后,线程会释放CPU,给其他进程。
- 加锁失败时,会从 用户态 陷入 内核态,让内核帮我们切换线程.
- 会有两次线索上下文切换的成本:运行->堵塞;堵塞->就绪。
- 所以,如果被锁住的代码执行时间很短,就应该选自旋锁。
自旋锁:
- 在 用户态 完成加锁和解锁。
读写锁:
- 当写锁没有被线程持有时,多个线程能并发持有读锁;
- 当写锁被某线程持有后,其他线程获取读锁或写锁的操作会被堵塞。
读优先:
写优先:
公平读写锁: 用队列把获取锁的线程排队,不管是写线程还是读线程都按FIFO原则加锁。
悲观锁:
互斥锁,自选锁,读写锁都属于悲观锁。 悲观锁认为并发访问共享资源时,冲突概率高;所以在访问前必须先加锁。
乐观锁:
若冲突概率非常低,可以使用乐观锁。 它的工作方式是,在访问共享资源时,不用先加锁;修改完资源后,再验证这段时间内有无冲突;若没有其他线程修改资源,则操作完成;否则放弃本次操作。
调度算法
进程调度算法
见 操作系统(上)。
页面置换算法
缺页中断
若第4步找不到空闲的物理页,则需要 页面置换算法 。
页表项:
- 状态位:是否在物理内存中。
- 访问字段:记录访问次数,供置换算法参考。
- 修改位:调入内存后,是否被修改过;由于内存中的每一页都在磁盘上保留一份副本,如果没修改过,则置换该页时就不需要将该页写回磁盘。
最佳页面置换算法(OPT)
置换在未来最长时间不访问的页面。
不可能实现。衡量其他算法效率用。
先进先出置换算法(FIFO)
最近最久未使用的置换算法(LRU)
时钟页面置换算法(Lock)
最不常用置换算法(LFU)
选择 访问次数 最少的页面并淘汰。
磁盘调度算法
磁盘由盘片,磁道,扇区组成。寻道是最耗时的部分。
以98,183,37,122,14,124,65,67为例:
先来先服务算法
最短寻道时间优先算法
优先选择离磁头最近的请求。
扫描算法SCAN
磁头在一个方向上移动,直到到达该方向最后的磁道,才调头。
循环扫描算法C-SCAN
掉头时迅速回到最边缘磁道,且该过程不响应请求。
LOOK与C-LOOK算法
针对SCAN算法的优化叫LOOK算法;针对C-SCAN算法的优化叫C-LOOK算法;仅仅移动到最远的请求位置,然后立即反向移动。