неустранимая ошибка при решении проблемы взаимоблокировки с помощью семафоров

#go

#Вперед

Вопрос:

Я изучаю Go. Я пытаюсь решить проблему взаимоблокировки в подпрограммах, используя семафоры. Я создал две функции, которые записывают данные в небуферизованный канал. чтение происходит по основному каналу. предполагается, что третья функция закрывает канал. когда я запускаю программу, она выдает эту ошибку неустранимая ошибка: все программы находятся в спящем режиме — взаимоблокировка! может кто-нибудь объяснить мне, почему это не работает.

 import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup
var s = []string{"a", "b", "c", "d"}
var w = []string{"w", "x", "t", "z", "p"}

func f(c chan string, ch chan bool) {
    for _, word := range s {
        c <- word
    }
    fmt.Println("f about to exit")
    ch <- true
    wg.Done()
}
func g(c chan string, ch chan bool) {
    for _, word := range w {
        c <- word
    }
    fmt.Println("g about to exit")
    ch <- true
    wg.Done()
}
func f1(ch chan string, c chan bool) {
    <-c
    <-c
    fmt.Println("about to close channel")
    close(ch)
}

func main() {
    ch := make(chan string)
    c := make(chan bool)
    wg.Add(3)
    go f(ch, c)
    go g(ch, c)
    go f1(ch, c)
    for word := range ch {
        fmt.Println(word)
    }

    wg.Wait()
}
  

Ответ №1:

Вы должны быть последовательны в именовании канала данных и канала сигнализации.
Вот и все.

Здесь у вас есть две процедуры перехода производителя и одна процедура перехода потребителя,
поэтому позвольте мне сначала упростить ваш код, попробуйте это онлайн:

 func main() {
    ch := make(chan string)
    done := make(chan struct{})
    go func() {
        for _, word := range []string{"1", "2", "3"} {
            ch <- word
        }
        done <- struct{}{}
    }()
    go func() {
        for _, word := range []string{"10", "20", "30", "40"} {
            ch <- word
        }
        done <- struct{}{}
    }()
    go func() {
        <-done
        <-done
        close(ch)
    }()
    for word := range ch {
        fmt.Print(word, " ")
    }
    fmt.Println()
}
  

Вывод:

 1 2 10 3 20 30 40 
  

Примечания:

  1. Вам не нужно sync.WaitGroup , поскольку для передачи сигналов достаточно готового канала.
  2. Пустая структура достаточно хороша для сигнализации.
  3. После получения двух сигналов done вы можете закрыть канал передачи данных, чтобы основной выходил из цикла.

Ответ №2:

После запуска трех подпрограмм вы читаете из канала, ch пока ch он не будет закрыт. Все функции получают ch в качестве первого аргумента, в который они записывают, но f1 записывают в свой второй аргумент, а не в первый. То есть f1 выполняется запись в канал c в main, поэтому f1 блокируется при первой записи, потому что никто не читает из c . Основная подпрограмма заблокирована для чтения из, ch потому что после того, как f и g выполнены, в нее никто не записывает.

Это похоже на путаницу в именовании канала: две функции получают (c, ch) , одна получает (ch, c) , но все вызываются с помощью (ch, c) .