Skip to content

Latest commit

 

History

History
81 lines (50 loc) · 5.1 KB

lock.md

File metadata and controls

81 lines (50 loc) · 5.1 KB
操作系统

自旋锁

Linux 自旋锁spinlock 同一时刻只能被一个可执行线程拥有, 当一个线程视图获取已经被持有的spin lock时候, 就会一直循环 -> 选择 等待锁重新可用。忙等待免去了线程挂起再被唤醒的转换。省去了两次上下文切换的时间,因此spinlock 适合下面的场景:

多核系统中, 持有自旋锁的时间小于完成两次上下文切换的时间, 这种场景使用spinlock 效率会比较高。

linux spinlock 在内核中相对比较常见, 因为内核中有很多需要短期加锁的场景, 比如在SMP系统中系统的中断上下文, 但是单核CPU 或者禁止内核抢占时候, 编译的时候自旋锁会被完全提出内核。

这点不难理解, 本质上串行执行任务时候, 靠时间分片执行各个任务来实现并发,所以单核使用spinlock 会忙等待到时间片用完, 得不偿失。

产生死锁的必要条件

互斥条件 一个资源只能被一个线程使用 请求与保持条件 一个线程因为请求资源而阻塞时候,对已经获得的资源保持不放 不可剥夺条件 线程已经获得的资源,在未使用完之前,不能强行剥夺 循环等待条件 若干线程之间形成一种头尾相接的循环等待资源关系

首先前三个 都是独占锁的特点之一,互斥,请求与保持,不可剥夺,唯一第四点是需要额外记忆的。

什么是死锁

我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行。如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进;对于B车来说,它走过桥面右边的一段路(即占有了桥的一部分资源),要想过桥还须等待A车让出左边的桥面,此时B车也不能前进。两边的车都不倒车,结果造成互相等待对方让出桥面,但是谁也不让路,就会无休止地等下去。这种现象就是死锁。如果把汽车比做进程,桥面作为资源,那麽上述问题就描述为:进程A占有资源R1,等待进程B占有的资源Rr;进程B占有资源Rr,等待进程A占有的资源R1。而且资源R1和Rr只允许一个进程占用,即:不允许两个进程同时占用。结果,两个进程都不能继续执行,若不采取其它措施,这种循环等待状况会无限期持续下去,就发生了进程死锁。

所谓死锁,是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面

如何避免死锁

如何避免死锁,只要打破产生死锁4个必要条件的任何一个,就可以避免死锁。

打破互斥条件 即允许多个线程同时访问某些资源,但是某些资源就是不让同时访问,这是由资源本身的属性决定的,所以这种方法没有卵用

打破不可剥夺条件 即允许线程强行从占有者那里夺取某些资源,就是说,当一个进程已占有了某些资源,它又申请新的资源,但不能立即被满足时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。

打破请求与保持 在采用这种方法时,系统规定所有线程在开始运行之前,都必须一次性的申请其在整个运行过程中所需的全部资源。 优点:简单,易于实现且安全 缺点:资源被严重浪费,线程运行被延迟

打破 循环等待 按照这种方法,系统将所有资源按类型进行线性排队,并赋予不同的序号 优点: 资源利用率和系统吞吐量都有明显提升 缺点: 资源序号必须相对稳定,限制了新类型设备的增加 限制用户简单自主的编程 会对资源造成浪费

死锁的检测

1 系统必须保持相关资源的请求和分配信息, 2 系统必须提供一种算法,然后利用这些信息来检测系统是否进入死锁状态

死锁的解除

1 剥夺资源 2 撤销线程

golang 的死锁检测

举例

func main() {
    c := make(chan int, 2)
    c <- 1
    fmt.Println("input 1")
    c <- 2
    fmt.Println("input 2")
    c <- 3
    fmt.Println("input 3")
}

运行结果

input 1
input 2
fatal error all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /Users/zhangjie/Downloads/test/test/test.go:22 +0x10d
exit status 2

可见 在运行到 c <-3 这段代码时候, 会报死锁的错误,因为golang 本身会通过一定的算法和资源分配和使用的情况来判断是否已经死锁,如果死锁会直接报错

从程序代码中可以看出,对于channel 写到第三个数据时候,因为已经没有任何一个goroutine来消费这个channel,按道理c <- 3 这里会永远的卡在这,所以程序直接报错 死锁的错误