Один канал с одним получателем и неизвестным количеством отправителей goroutines, вызывающих тупик - PullRequest
0 голосов
/ 13 января 2019

У меня есть один канал, и приемник является основным. Я порождаю несколько goroutines, каждая из которых отправляет строку по каналу.

Теперь, это вызывает тупик, потому что я не закрыл канал должным образом, используя функцию закрытия. Дело в том, что я понятия не имею, сколько будет создано подпрограмм, поэтому нет способа узнать, когда закрывать канал.

Я пытался использовать WaitGroup, проблема в том, что я прочитал, что я не могу использовать Add в goroutine и что я должен использовать wg.Add (1) в основном процессе / goroutine, я пытался использование Add в родительском goroutine, порождающем дочерний goroutine, что также вызывает тупик

основной пакет

import (
    "fmt"
    "sync"
)

var i = 0

func doSomething(ch chan string, wg sync.WaitGroup) {
    defer wg.Done()
    ch <- fmt.Sprintf("doSomething: %d", i)
    i++
    if i == 10 {return}
    wg.Add(1)
    go doSomething(ch, wg)
}

func main() {
    ch := make(chan string)
    var wg sync.WaitGroup
    wg.Add(1)
    go doSomething(ch, wg)
    wg.Wait()
    for s := range ch {
        fmt.Println(s)
    }
}

Теперь, это всего лишь тестовый код, поэтому предположим, что мы не знаем, что мы создадим только 10 процедур, предположим, что он неизвестен во время выполнения, здесь я получаю тупиковую ошибку мгновенно без вывода, если я не не использую WorkGroup Я получаю сообщение об ошибке перед печатью 10-й строки (потому что я не закрывал канал)

Я также пытался не создавать goroutine для каждого вызова функции и вместо этого использовать одну goroutine для всех рекурсивных вызовов (начиная с main), и чтобы закрыть канал, я создал анонимную функцию для go, которая сначала вызывает doSomething Затем функция вызывает close, поэтому все рекурсивные вызовы будут оценены, и мы точно будем знать, когда закрывать канал. Но теперь это то, чего я пытаюсь достичь, я пытаюсь заставить неизвестное количество горутин работать вместе и закрыть канал после того, как они все как-то закончили.

1 Ответ

0 голосов
/ 13 января 2019

Есть несколько вопросов.

Во-первых, программа копирует значения группы ожидания при передаче их в качестве аргументов. Группы ожидания не работают правильно при копировании. Вместо этого передайте указатель в группу ожидания.

Вторая проблема заключается в том, что main ожидает завершения всех процедур, прежде чем получать значения из каналов. Поскольку буфер канала недостаточно велик для хранения всех отправленных значений, программа блокируется.

Третья проблема заключается в том, что основной диапазон располагается по каналу, но ничто никогда не закрывает канал. В результате Main не выйдет.

Чтобы исправить вторую и третью проблемы, запустите другую процедуру, ожидающую doSomthing с, и закройте канал, когда они будут завершены.

Попробуйте это:

func doSomething(ch chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    ch <- fmt.Sprintf("doSomething: %d", i)
    i++
    if i == 10 {
        return
    }
    wg.Add(1)
    go doSomething(ch, wg)
}

func main() {
    ch := make(chan string)
    var wg sync.WaitGroup
    wg.Add(1)
    go doSomething(ch, &wg)
    go func() {
        wg.Wait()
        close(ch)
    }()
    for s := range ch {
        fmt.Println(s)
    }
}
...