Подождите go подпрограмм до конца sh, затем прочитайте из канала - PullRequest
0 голосов
/ 11 февраля 2020

Как мне дождаться, пока все go подпрограмм завершатся sh, а затем прочитать все данные из канала?

Почему этот пример застрял в ожидании процедуры go до конца sh?

Go Детская площадка

package main

import (
    "fmt"
    "sync"
    "time"
)

func doStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
    defer wg.Done()
    time.Sleep(time.Duration(i) * time.Second)
    messages <- fmt.Sprintf("Doing stuff...%d", i)

}

func doMoreStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
    defer wg.Done()
    time.Sleep(time.Duration(i) * time.Second)
    messages <- fmt.Sprintf("Doing more stuff...%d", i)
}

func main() {
    var wg sync.WaitGroup
    var messages = make(chan string)

    for start := time.Now();; {
        elapsedTime := time.Since(start)
        fmt.Println(elapsedTime)
        if elapsedTime > time.Duration(3) * time.Second {
            fmt.Println("BREAK")
            break
        }

        for i := 0; i < 10; i++ {
            wg.Add(1)
            go doStuff(i, &wg, messages)

            wg.Add(1)
            go doMoreStuff(i, &wg, messages)
        }
        time.Sleep(time.Duration(1) * time.Second)
    }
    fmt.Println("WAITING")
    wg.Wait()
    fmt.Println("DONE")
    for message := range messages {
        fmt.Println(message)
    }
}

Ответы [ 2 ]

2 голосов
/ 11 февраля 2020

Если вы хотите дождаться, пока все процедуры завершатся sh, которые отправляют сообщения по каналу, и после этого вы хотите начать чтение сообщений, тогда у вас нет другого выбора, кроме как использовать буферизованный канал, который может "принимать" все сообщения, которые могут быть отправлены на него горутинами.

Это не что-то практичное. И даже если бы вы go пошли по этому пути, вы могли бы получать и распечатывать сообщения, но выполнение for range l oop никогда не прекратится, потому что это прекращается только тогда, когда он получает все отправленные сообщения. на канале, прежде чем он был закрыт, но вы никогда не закроете канал. Вы можете проверить это «полуработающее» решение на Go Playground .

. Вместо этого запустите программу, которая ждет, пока другие завершат sh, а затем закройте канал:

go func() {
    fmt.Println("WAITING")
    wg.Wait()
    close(messages)
}()

Так что теперь вы можете использовать for range для получения сообщений в main goroutine:

for message := range messages {
    fmt.Println(message)
}

fmt.Println("DONE")

Попробуйте это на Go Playground .

Это решение все еще не идеально: оно сначала должно запустить все процедуры, и только затем оно пытается получить значения, и все запущенные программы будут заблокированы при отправке (до готовности main). чтобы получить их).

Лучшим решением может быть запуск процедуры, которая получает значения, предпочтительно перед процедурами, которые отправляют сообщения по каналу (иначе они все равно будут заблокированы при отправке):

go func() {
    for message := range messages {
        fmt.Println(message)
    }
}()

И дождитесь выполнения программ и закройте канал в конце main():

fmt.Println("WAITING")
wg.Wait()
close(messages)

Проблема в том, что когда main() заканчивается, то и ваше приложение, оно не ждет других не main Горутин до фини sh. А это значит, что он не будет ждать, пока «потребительская» программа получит сообщения.

Чтобы дождаться этого «потребителя», вы можете использовать дополнительные sync.WaitGroup:

var wg2 sync.WaitGroup
wg2.Add(1)
go func() {
    defer wg2.Done()
    for message := range messages {
        fmt.Println(message)
    }
}()

// And the end of `main()`:

fmt.Println("WAITING")
wg.Wait()
close(messages)

fmt.Println("DONE")
wg2.Wait()

Попробуйте это на Go Детская площадка .

0 голосов
/ 11 февраля 2020

Пример застрял, потому что вы используете небуферизованный канал. Обе go -программы блокируются в ожидании записи в канал, так как ничего не готово для чтения с него.

Вы можете использовать буферизованный канал, но тогда вы просто используете его в качестве хранилища данных. Каналы более полезны для общения. Вопрос в том, почему вы хотите дождаться окончания работы всех авторов? В общем случае неизвестного (неограниченного) количества авторов вы не знаете, насколько велик ваш канал.

...