Sobes.tech
Junior

Deadlock дар вақти кор кардани goroutine чист?

sobes.tech AI

Ҷавоб аз AI

Deadlock (блокировка) — это ситуация, когда две или более горутин блокируют друг друга, ожидая друг друга, и ни одна из них не может продолжить выполнение. Чаще всего возникает при работе с мьютексами или каналами.

Примеры deadlock:

  1. Блокировка мьютексов в разном порядке:

    // Пример deadlock с мьютексами
    var mu1 sync.Mutex
    var mu2 sync.Mutex
    
    func goroutine1() {
        mu1.Lock() // Захватывает mu1
        time.Sleep(100 * time.Millisecond) // Ждет немного
        mu2.Lock() // Пытается захватить mu2 (занята goroutine2)
        fmt.Println("goroutine1 захватила mu2")
        mu2.Unlock()
        mu1.Unlock()
    }
    
    func goroutine2() {
        mu2.Lock() // Захватывает mu2
        time.Sleep(100 * time.Millisecond) // Ждет немного
        mu1.Lock() // Пытается захватить mu1 (занята goroutine1)
        fmt.Println("goroutine2 захватила mu1")
        mu1.Unlock()
        mu2.Unlock()
    }
    

    Goroutine1 захватила mu1 и ждет mu2. Goroutine2 захватила mu2 и ждет mu1. Обе горутины заблокированы.

  2. Отправка в небуферизованный канал без получателя:

    // Пример deadlock с каналом
    func main() {
        ch := make(chan int)
        //go func() { // Если не запустить горутину для чтения, будет deadlock
        //	<-ch
        //}()
        ch <- 1 // Отправка в канал без активного получателя
        fmt.Println("Отправлено в канал") // Эта строка не будет достигнута
    }
    

    Отправка в небуферизованный канал блокируется до тех пор, пока другая горутина не начнет читать из него. Если получателя нет, происходит deadlock.

  3. Чтение из канала без отправителя (после закрытия или пустой буферизованный):

    // Пример deadlock при чтении из канала
    func main() {
        ch := make(chan int, 1) // Буферизованный канал на 1 элемент
        ch <- 1 // Отправили 1
        close(ch) // Закрыли канал
        <-ch // Прочитали 1
        <-ch // Пытаемся прочитать снова из закрытого и пустого канала - deadlock
    }
    

    Чтение из закрытого непустого канала завершится успешно. Чтение из закрытого и пустого или из открытого, но пустого небуферизованного канала без отправителя приведет к блокировке.

Golang-рантайм может обнаружить определенные типы deadлоков (например, когда все заблокированные горутины ожидают получения или отправки единственной горутины), и в таком случае программа аварийно завершится с сообщением "fatal error: all goroutines are asleep - deadlock!". Однако, не все deadlockи могут быть автоматически обнаружены.

Для предотвращения deadlockов важно:

  • Соблюдать последовательный порядок захвата мьютексов.
  • Убедиться, что для каждой отправки в небуферизованный канал есть соответствующее чтение, и наоборот.
  • При работе с каналами использовать select с default веткой или тайм-аутами при необходимости.
  • Аккуратно управлять временем жизни горутин и закрытием каналов.