golang mutex 执行流程

Python020

golang mutex 执行流程,第1张

自旋:busy-watting 快,但是占用cpu

互斥:sleep-watting 慢(内核上下文切换),但是长期等待优势,不占用cpu

golang的mutex有自旋也有互斥

流程:

一开始正常模式,尝试获取锁,如果获取失败那么尝试自旋busy-waitting(至少有个本地的P队列,并且本地的P队列可运行G队列为空;即保证至少有一个核让其自旋;且自旋也不是直接忙等,是使用底层的pause指令;),自旋超过一定限制,进入队列,休眠等待信号,sleep-watting

此时队列和新的自旋协程竞争,当对头经过时间t后却拿不到锁,那么进入饥饿模式,即所有新的竞争者入队列排队,直到队列头部获取锁时间小于t,然后恢复到正常模式。

参考:

https://zhuanlan.zhihu.com/p/27608263

https://blog.csdn.net/thatboyNB/article/details/116998612?utm_source=app&app_version=4.16.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

在Golang中进行开发时,互斥锁在不断尝试获取永远无法获取的锁时会遇到 饥饿 问题。在本文中,我们将探讨影响Go 1.8的饥饿问题,该问题已在Go 1.9中解决。

为了说明互斥锁的饥饿状况,我将以 拉斯·考克斯 ( Russ Cox)提出的 关于他们讨论互斥锁改进的 问题 为例:

starvation.go

此示例基于两个goroutine:

两者都具有100微秒的周期,但是由于goroutine 1一直在请求锁定,因此可以预期它将更频繁地获得锁定。

这是一个用Go 1.8进行的示例,该示例具有10次迭代的循环的锁分配:

该互斥锁已被第二个goroutine捕获了十次,而第一个则超过了700万次。让我们分析一下这里发生了什么。

首先,goroutine 1将获得锁定并睡眠100微秒。当goroutine 2尝试获取锁时,它将被添加到锁的队列(FIFO顺序)中,并且goroutine将进入等待状态:

Figure 1 — lock acquisition

然后,当goroutine 1完成工作时,它将释放锁定。此版本将通知队列唤醒goroutine 2。Goroutine 2将被标记为可运行,并正在等待Go Scheduler在线程上运行:

Figure 2— goroutine 2 is awoke

但是,在goroutine 2等待运行时,goroutine 1将再次请求锁定:

Figure 3— goroutine 2 is waiting to run

当goroutine 2尝试获取锁时,它将看到它已经具有保持状态并进入等待模式,如图2所示:

Figure 4— goroutine 2 tries again to get the lock

goroutine 2对锁的获取将取决于它在线程上运行所花费的时间。

现在已经确定了问题,让我们回顾可能的解决方案。

处理互斥量的方法有很多,例如:

barging mode

Go 1.8就是这样设计的,它反映了我们之前看到的内容。

handoff mode

我们可以 在Linux内核 的 互斥体中 找到此逻辑:

在我们的情况下,互斥锁切换会完美平衡两个goroutine之间的锁分配,但是会降低性能,因为这将迫使第一个goroutine即使未持有也要等待锁。

spinning mode

Go 1.8也使用此策略。当试图获取已经持有的锁时,如果本地队列为空且处理器数量大于一,则goroutine将旋转几次-如果仅使用一个处理器旋转就会阻塞程序。旋转后,goroutine将停放。如果程序大量使用锁,它可以作为快速路径。

有关如何设计锁的更多信息( 插入 ,越区切换,自旋锁),通常, Filip Pizlo撰写 了必读的文章“WebKit中的锁定 ”。

在Go 1.9之前,Go结合了插入和旋转模式。在1.9版中,Go通过添加新的饥饿模式解决了先前解释的问题,该模式将导致在解锁模式期间进行切换。

所有等待锁定时间超过一毫秒的goroutine,也称为 有界等待 ,将被标记为饥饿。当标记为饥饿时,解锁方法现在将把锁直接移交给第一位服务员。这是工作流程:

starvation mode

由于进入的goroutine将不会获取任何为下一个服务员保留的锁,因此在饥饿模式下也将禁用旋转。

让我们使用Go 1.9和新的starvation模式运行前面的示例:

现在的结果更加公平。现在,我们想知道新的控制层是否会对互斥体不处于饥饿状态的其他情况产生影响。正如我们在该程序包的基准测试(Go 1.8与Go 1.9)中所看到的,在其他情况下,性能并没有下降(不同处理器数量下, 性能会略有变化 ):

翻译自: https://medium.com/a-journey-with-go/go-mutex-and-starvation-3f4f4e75ad50