What is a deadlock when working with goroutines?
sobes.tech AI
Answer from AI
Deadlock (stalemate) is a situation where two or more goroutines are blocked, waiting for each other, and none of them can continue execution. It most often occurs when working with mutexes or channels.
Examples of deadlock:
-
Locking mutexes in different orders:
// Example of deadlock with mutexes var mu1 sync.Mutex var mu2 sync.Mutex func goroutine1() { mu1.Lock() // Locks mu1 time.Sleep(100 * time.Millisecond) // Waits a bit mu2.Lock() // Tries to lock mu2 (occupied by goroutine2) fmt.Println("goroutine1 acquired mu2") mu2.Unlock() mu1.Unlock() } func goroutine2() { mu2.Lock() // Locks mu2 time.Sleep(100 * time.Millisecond) // Waits a bit mu1.Lock() // Tries to lock mu1 (occupied by goroutine1) fmt.Println("goroutine2 acquired mu1") mu1.Unlock() mu2.Unlock() }Goroutine1 has locked
mu1and is waiting formu2. Goroutine2 has lockedmu2and is waiting formu1. Both goroutines are blocked. -
Sending to an unbuffered channel without a receiver:
// Example of deadlock with channel func main() { ch := make(chan int) //go func() { // If no goroutine is started for reading, deadlock occurs // //<-ch //}() ch <- 1 // Sending to a channel without an active receiver fmt.Println("Sent to channel") // This line will not be reached }Sending to an unbuffered channel blocks until another goroutine starts reading from it. If there is no receiver, deadlock occurs.
-
Reading from a channel without a sender (after closing or empty buffered):
// Example of deadlock when reading from a channel func main() { ch := make(chan int, 1) // Buffered channel with capacity 1 ch <- 1 // Sent 1 close(ch) // Closed the channel <-ch // Read 1 <-ch // Attempt to read again from a closed and empty channel - deadlock }Reading from a closed non-empty channel will succeed. Reading from a closed and empty channel or from an open but empty unbuffered channel without a sender will block.
The Go runtime can detect certain types of deadlocks (for example, when all blocked goroutines are waiting to receive or send on a single goroutine), and in such cases, the program will terminate with the message "fatal error: all goroutines are asleep - deadlock!". However, not all deadlocks can be automatically detected.
To prevent deadlocks, it is important to:
- Maintain a consistent order of mutex acquisition.
- Ensure that for every send to an unbuffered channel, there is a corresponding receive, and vice versa.
- Use
selectwithdefaultcase or timeouts when working with channels if necessary. - Carefully manage the lifetime of goroutines and closing of channels.