英文:
when we will use not re-entrance lock
问题
作为 Golang 的初学者,我发现《Go 程序设计语言》一书中提到的 Golang 的互斥锁(mutex)不支持重入。他们解释了原因:
> Golang 的互斥锁不支持重入是有充分理由的。互斥锁的目的是确保在程序执行的关键点上,共享变量的某些不变量得到维护。其中一个不变量是“没有 goroutine 正在访问共享变量”,但是互斥锁可能还要保护其他特定于数据结构的不变量。当一个 goroutine 获取了互斥锁之后,它可以假设这些不变量成立。在持有锁的期间,它可以更新共享变量,从而暂时违反这些不变量。然而,当它释放锁时,它必须保证恢复了顺序,并且这些不变量再次成立。尽管重入锁可以确保没有其他 goroutine 访问共享变量,但它无法保护这些变量的其他不变量。
看起来这个解释已经足够详细了,但不幸的是我还是不太理解。我仍然不知道在什么情况下我们应该使用不支持重入的互斥锁,如果不使用会有什么坏的结果。如果有人能给我一个例子,我将不胜感激。
英文:
As a beginner of golang, I find the mutex of golang is not re-entranceLock from the book The Go Programming Language
. And they explain the reason as:
>There is a good reason Go’s mutexes are not re-entrant. The purpose of a mutex is to ensure that certain invariants of the shared variables are maintained at critical points during program execution. One of the invariants is "no goroutine is accessing the shared variables," but there may be additional invariants specific to the data structures that the mutex guards. When a goroutine acquires a mutex lock, it may assume that the invariants hold. While it holds the
lock, it may update the shared variables so that the invariants are temporarily violated.
However, when it releases the lock, it must guarantee that order has been restored and the invariants hold once again. Although a re-entrant mutex would ensure that no other goroutines are accessing the shared variables, it cannot protect the additional invariants of those variables.
it seemed the explain is detail enough, unfortunately I can't get it. I still don't know when we should use not re-entranceLock, and if not what bad surprise will I receive? I will appreciate if anyone can give me an example.
答案1
得分: 0
这是一个示例,用于说明问题的糟糕代码:
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
lock sync.Mutex
count int
enabled bool
NextValue func(int) int
}
const maxCount = 10
func (c *SafeCounter) Count() int {
return c.count
}
func (c *SafeCounter) Increment() {
c.lock.Lock()
if c.enabled {
c.count = c.NextValue(c.count)
}
c.lock.Unlock()
}
func (c *SafeCounter) SetEnabled(enabled bool) {
c.lock.Lock()
c.enabled = enabled
if !enabled {
c.count = 0
}
c.lock.Unlock()
}
func main() {
var counter SafeCounter
counter.SetEnabled(true)
counter.NextValue = func(value int) int {
if counter.Count() > maxCount {
// 安全计数器不希望在这里发生!
// 程序将在SetEnabled中发生panic
counter.SetEnabled(false)
}
return value + 1
}
for i := 0; i < 100; i++ {
doAction()
counter.Increment()
}
fmt.Println(counter.Count())
}
func doAction() {
// 一些操作
}
Increment
和SetEnabled
都会获取锁,因为它们不能在执行过程中允许enabled
和count
的值发生变化。然而,如果锁是可重入的(递归的),那么它将被允许(因为两个调用都在同一个goroutine上运行)。
英文:
Here's an example of <b>bad</b> code just to illustrate the issue:
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
lock sync.Mutex
count int
enabled bool
NextValue func(int) int
}
const maxCount = 10
func (c *SafeCounter) Count() int {
return c.count
}
func (c *SafeCounter) Increment() {
c.lock.Lock()
if c.enabled {
c.count = c.NextValue(c.count)
}
c.lock.Unlock()
}
func (c *SafeCounter) SetEnabled(enabled bool) {
c.lock.Lock()
c.enabled = enabled
if !enabled {
c.count = 0
}
c.lock.Unlock()
}
func main() {
var counter SafeCounter
counter.SetEnabled(true)
counter.NextValue = func(value int) int {
if counter.Count() > maxCount {
// Safe counter doesn't expect this here!
// The program will panic in SetEnabled
counter.SetEnabled(false)
}
return value + 1
}
for i := 0; i < 100; i++ {
doAction()
counter.Increment()
}
fmt.Println(counter.Count())
}
func doAction() {
// some action
}
Both Increment
and SetEnabled
acquire locks because they can't allow the values of enabled
and count
to change while they're in the middle of something. However, if the lock was re-entrant (recursive), then it would be allowed (since both calls run on the same goroutine).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论