I’m reading The Little Go Book.
Page 76 demonstrates how you can deadlock with a single lock:
var (
lock sync.Mutex
)
func main() {
go func() { lock.Lock() }()
time.Sleep(time.Millisecond * 10)
lock.Lock()
}
Running this results in a deadlock as explained by the author. However, what I don’t understand is why.
I changed the program to this:
var (
lock sync.Mutex
)
func main() {
go func() { lock.Lock() }()
lock.Lock()
}
My expectation was that a deadlock would still be thrown. But it wasn’t.
Could someone explain to me what’s happening here please?
The only scenario I can think of that explains this is the following (but this is guesswork):
First example
- The lock is acquired in the first goroutine
- The call to
time.Sleep()ensures the lock is acquired - The
mainfunction attempts to acquire the lock resulting in a deadlock - Program exits
Second example
- The lock is acquired in the first goroutine but this takes some time to happen (??)
- Since there is no delay the
mainfunction acquires the lock before the goroutine can - Program exits
>Solution :
In the first example, main sleeps long enough to give the child goroutine the opportunity to start and acquire the lock. That goroutine then will merrily exit without releasing the lock.
By the time main resumes its control flow, the shared mutex is locked so the next attempt to acquire it will block forever. Since at this point main is the only routine left alive, blocking forever results in a deadlock.
In the second example, without the call to time.Sleep, main proceeds straight away to acquire the lock. This succeeds, so main goes ahead and exits. The child goroutine would then block forever, but since main is exited, the program terminates without deadlock.