Ждите всех рутин, не блокируя - PullRequest
0 голосов
/ 27 апреля 2018

У меня есть вопрос по поводу стандартных процедур.

Мой код:

func main() {

    ok := make(chan bool, 1)

    i := 0
    fmt.Println("Starting...")
    for i <= 3 {
        fmt.Println("Loop: ", i)
        go long(ok, i)
        time.Sleep(1 * time.Second)
        i = i + 1

        select {
        case _ = <-ok:
        default:
            fmt.Println("Default")
        }
    }

    fmt.Println("Done...")

}

func long(c chan bool, i int){
    fmt.Println("Inside long: ", i)
    time.Sleep(3 * time.Second)
    fmt.Println("Done with loop: ", i)
    c <- true

}

Это дает мне вывод:

Starting...
Loop:  0
Inside long:  0
Default
Loop:  1
Inside long:  1
Default
Loop:  2
Inside long:  2
Done with loop:  0
Loop:  3
Inside long:  3
Done with loop:  1
Done...

Поскольку я использую default в select, канал не блокируется. И основная функция завершает работу, как и все текущие процедуры. Затем я читаю о sync и WaitGrops.

func main() {

    ok := make(chan bool, 1)

    var wg sync.WaitGroup

    i := 0
    fmt.Println("Starting...")
    for i <= 3 {
        fmt.Println("Loop: ", i)
        wg.Add(1)
        go long(ok, i)
        time.Sleep(1 * time.Second)
        i = i + 1

        select {
            case _ = <-ok:
            default:
                fmt.Println("Default")
        }
    }

    wg.Wait()
    fmt.Println("Done...")

}

Что дает мне:

Starting...
Loop:  0
Inside long:  0
Default
Loop:  1
Inside long:  1
Default
Loop:  2
Inside long:  2
Done with loop:  0
Loop:  3
Inside long:  3
Done with loop:  1
Done with loop:  2
Done with loop:  3

Теперь мы ближе к желаемому исполнению: Цикл for выполняет все вызовы функции, а затем я получаю результат async. Это было бы замечательно, если бы все это работало. Но генерируется ошибка:

fatal error: all goroutines are asleep - deadlock! 

Почему это так и как мне это исправить? (Можно ли исправить, не зная, сколько раз будет выполняться wg.Add ()?)

Ответы [ 3 ]

0 голосов
/ 27 апреля 2018

Итак, битный ответ удаляет канал, который решает вашу проблему.

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

0 голосов
/ 27 апреля 2018

Вы можете сделать это и с каналами, но это не так элегантно, как с WaitGroup, потому что требует немного больше усилий по синхронизации.

Для вашей проблемы выше, вы можете сделать что-то вроде этого:

package main

func main() {

    ok := make(chan bool, 1)
    done := make(chan bool)

    i := 0
    j := 0
    fmt.Println("Starting...")
    for i <= 3 {
        fmt.Println("Loop: ", i)
        go long(ok, i)
        time.Sleep(1 * time.Second)
        i = i + 1
    }

    go func() {

        for {
            select {
            case _ = <-ok:
                j++
                if j == 4 {
                    done <- true
                    return
                }
            }
        }
    }()

    <-done
    fmt.Println("Done...")

}

func long(c chan bool, i int) {
    fmt.Println("Inside long: ", i)
    time.Sleep(3 * time.Second)
    fmt.Println("Done with loop: ", i)
    c <- true

}
0 голосов
/ 27 апреля 2018

Вам нужно звонить Done() на WaitGroup столько раз, сколько вы звоните Add(1), чтобы wg.Wait() можно было разблокировать. Вам больше не нужен канал для синхронизации:

package main

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

func main() {
    var wg sync.WaitGroup

    i := 0
    fmt.Println("Starting...")
    for i <= 3 {
        fmt.Println("Loop: ", i)
        wg.Add(1)
        go long(&wg, i)
        time.Sleep(1 * time.Second)
        i = i + 1
    }
    wg.Wait()
    fmt.Println("Done...")
}

func long(wg *sync.WaitGroup, i int){
    fmt.Println("Inside long: ", i)
    time.Sleep(3 * time.Second)
    fmt.Println("Done with loop: ", i)
    wg.Done()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...