「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」。
上篇文章讲了sellock,接下来接着继续看
selunlock
func selunlock(scases []scase, lockorder []uint16) {
for i := len(lockorder) - 1; i >= 0; i-- {
c := scases[lockorder[i]].c
if i > 0 && c == scases[lockorder[i-1]].c {
continue // will unlock it on the next iteration
}
unlock(&c.lock)
}
}
首先第一个看到的问题就是相对于sellock,他的遍历顺序恰恰相反,这里猜测是像for循环的大括号一样(不恰当的比喻,不过之前操作系统的加锁解锁方式也遵循这种规律)
照例也有了
if i > 0 && c == scases[lockorder[i-1]].c {
continue // will unlock it on the next iteration
}
这部分当然也是为了防止重复解锁,不过作者在注释中特意强调了一定要注意不要重复解锁,因为一个通道在被解锁后可能就被释放了。
selparkcommit
func selparkcommit(gp *g, _ unsafe.Pointer) bool {
gp.activeStackChans = true
atomic.Store8(&gp.parkingOnChan, 0)
var lastc *hchan
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
if sg.c != lastc && lastc != nil {
unlock(&lastc.lock)
}
lastc = sg.c
}
if lastc != nil {
unlock(&lastc.lock)
}
return true
}
gp.activeStackChans = true,第一行就看不懂了,查了下gp可以理解为是一个stack,activeStackChans表示有未锁定的通道指向这个协程的堆栈,当他为true的时候,代表如果进行堆栈复制的话需要我们先锁通道再进行堆栈复制。
atomic.Store是往第一个参数里写入第二个参数的值,是一个原子操作。store8代表参数类型为unit8。
ParkingOnChan 表示 goroutine 即将停在 chansend 或 chanrecv 上。 用于表示堆栈收缩的不安全点。 它是一个布尔值,但会原子的更新。
一旦我们解锁这个通道,该通道里任何sudog中的字段都有可能会更改,由于多个sudog可能有相同的通道,因此我们只有在通道的最后一个实例才可以解锁。