sync.Waitgroup не блокирует выполнение - PullRequest
0 голосов
/ 11 января 2019

Рассмотрим этот фрагмент кода

package main

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

func main() {
    wg := new(sync.WaitGroup)
    nap := func() {
        wg.Add(1)
        time.Sleep(2 * time.Second)
        fmt.Println("nap done")
        wg.Done()
    }

    go nap()
    go nap()
    go nap()

    fmt.Println("nap time")
    wg.Wait()
    fmt.Println("all done")
}

Запуск такого кода дает ожидаемый результат:

nap time
nap done
nap done
nap done
all done

Теперь давайте пропустим первую стандартную выходную печать перед wg.Wait():

// fmt.Println("nap time")
wg.Wait()
fmt.Println("all done")

Вывод теперь меняется на неожиданный:

all done

Где ожидается, будет:

nap done
nap done
nap done
all done

Тот же код на детской площадке выдает этот вывод без необходимости пропуска вывода stdout.

Можете ли вы объяснить мне, что мне там не хватает?

1 Ответ

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

Несмотря на то, что это похоже на магию, оно имеет логическое объяснение. Go не гарантирует порядок выполнения goroutines. В данном фрагменте кода появилось три goroutines, но на самом деле их четыре: самая первая, которая создается при запуске.

Stdout print опущен

Эта рутина породила три функции сна и продолжила свой план. Это было так быстро, что он выполнил wg.Wait() до того, как любая из порожденных горутин могла вызвать wg.Add(1). В результате wg.Wait() не заблокировал выполнение и программа завершилась.

Печать на стандартный вывод до wg.Wait()

В этом случае выполнение программы было другим, программы могли выполнять вызов wg.Add(1), потому что основная программа была не быстрой, как в первом случае. Такое поведение не гарантируется, что можно увидеть на примере связанной игровой площадки.

Это не имеет ничего общего с выводом stdout

Следующий пример кода даст тот же ожидаемый результат:

time.Sleep(time.Second)
wg.Wait()
fmt.Println("all done")

fmt.Println() оказал такое же воздействие, как time.Sleep().

Идиоматический способ

Правило простое: позвоните wg.Add(1) перед тем, как порождать горутин.

package main

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

func main() {
    wg := new(sync.WaitGroup)
    nap := func() {
        time.Sleep(2 * time.Second)
        fmt.Println("nap done")
        wg.Done()
    }

    napCount := 3
    wg.Add(napCount)
    for i := 0; i < napCount; i++ {
        go nap()
    }

    wg.Wait()
    fmt.Println("all done")
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...