Ответ
Deadlock (взаимная блокировка) — это ситуация в конкурентном программировании, когда две или более горутины оказываются в состоянии бесконечного ожидания, поскольку каждая из них удерживает ресурс, необходимый другой горутине, и ждет освобождения ресурса, захваченного другой.
Это приводит к полной остановке выполнения задействованных горутин. Среда выполнения Go способна обнаруживать такие ситуации и аварийно завершать программу с выводом состояния всех горутин, что помогает в отладке.
Классический пример:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mu1, mu2 sync.Mutex
go func() {
fmt.Println("Горутина 1: пытается захватить mu1")
mu1.Lock()
fmt.Println("Горутина 1: захватила mu1")
time.Sleep(100 * time.Millisecond)
fmt.Println("Горутина 1: пытается захватить mu2")
mu2.Lock() // Бесконечно ждёт mu2, которую удерживает Горутина 2
defer mu2.Unlock()
defer mu1.Unlock()
}()
go func() {
fmt.Println("Горутина 2: пытается захватить mu2")
mu2.Lock()
fmt.Println("Горутина 2: захватила mu2")
time.Sleep(100 * time.Millisecond)
fmt.Println("Горутина 2: пытается захватить mu1")
mu1.Lock() // Бесконечно ждёт mu1, которую удерживает Горутина 1
defer mu1.Unlock()
defer mu2.Unlock()
}()
time.Sleep(2 * time.Second) // Даём горутинам время на deadlock
fmt.Println("Программа завершена (эта строка не будет достигнута)")
}
Способы предотвращения:
- Соблюдение порядка блокировок (Lock Ordering): Всегда захватывать мьютексы в строго определенном порядке. Например, сначала
mu1
, потомmu2
во всех горутинах. Это исключает возможность циклического ожидания. - Использование
sync.RWMutex
: Позволяет множественные блокировки на чтение, снижая вероятность конфликтов, если операции в основном читающие. - Применение таймаутов: Использовать
select
сtime.After
при работе с каналами илиsync.Mutex.TryLock
(появился в Go 1.18) для попытки захвата мьютекса без блокировки. - Использование каналов: Вместо мьютексов и общего состояния, можно организовать коммуникацию через каналы, передавая владение данными от одной горутины к другой. Это один из идиоматических подходов в Go.