Расхождения между Go Playground и Go на моей машине?

Чтобы уладить некоторые недоразумения, которые у меня есть по поводу подпрограмм, я пошел на игровую площадку Go и запустил этот код:

package main

import (
    "fmt"
)

func other(done chan bool) {
    done <- true
    go func() {
        for {
            fmt.Println("Here")
        }
    }()
}

func main() {
    fmt.Println("Hello, playground")
    done := make(chan bool)
    go other(done)
    <-done
    fmt.Println("Finished.")
}

Как я и ожидал, игровая площадка Go вернулась с ошибкой: процесс занял слишком много времени.

Это, кажется, подразумевает, что горутин, созданный в other работает вечно

Но когда я запускаю тот же код на своей машине, я получаю этот вывод почти мгновенно:

Hello, playground.
Finished.

Кажется, это подразумевает, что other выходит, когда основная программа завершена. Это правда? Или основная программа завершается, а другая продолжает работать в фоновом режиме?

2 ответа

Решение

Объяснение того, что вы видите:

На игровой площадке Go GOMAXPROCS 1 ( доказательство).

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

Ваш код (как и каждое приложение Go) начинается с выполнения программы main() функция (основная программа). Это запускает другую программу, которая выполняет other() функция, то она получает от done канал - какие блоки. Таким образом, планировщик должен переключиться на другую программу (выполнение other() функция).

В вашем other() функция при отправке значения на done канал, который делает оба тока (other()) и main Горутин работает. Планировщик выбирает продолжить работу other(), и с тех пор GOMAXPROCS=1, main() не продолжается Сейчас other() запускает другую программу, выполняющую бесконечный цикл. Планировщик выбирает выполнение этой процедуры, которая занимает вечно, чтобы добраться до заблокированного состояния, поэтому main() не продолжается

И тогда тайм-аут песочницы Go Playground приходит как отпущение грехов:

процесс занял слишком много времени

Обратите внимание, что модель памяти Go гарантирует только то, что определенные события происходят раньше других, у вас нет гарантии того, как выполняются 2 одновременных программы. Что делает вывод недетерминированным.

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

Также обратите внимание, что выходные данные на игровой площадке Go кэшируются, поэтому, если вы снова запустите приложение, оно не будет запущено снова, а вместо этого кэшированные выходные данные будут представлены немедленно. Если вы изменяете что-либо в коде (например, вставляете пробел или комментарий), а затем запускаете его снова, оно затем компилируется и запускается снова. Вы заметите это по увеличенному времени отклика. Используя текущую версию ( Go 1.6), вы будете каждый раз видеть один и тот же результат.

Запуск локально (на вашей машине):

Когда вы запускаете его локально, скорее всего GOMAXPROCS будет больше чем 1 по умолчанию это число доступных ядер ЦП (начиная с версии 1.5). Так что не имеет значения, если у вас есть программа, выполняющая бесконечный цикл, другая программа будет выполняться одновременно, которая будет main(), и когда main() возвращается, ваша программа завершается; он не ждет другихmain выполнение процедур (см. Spec: Выполнение программы).

Также обратите внимание, что даже если вы установите GOMAXPROCS в 1Ваше приложение, скорее всего, закроется через "короткое" время, так как реализация планировщика переключится на другие процедуры, а не просто выполнит бесконечный цикл навсегда (однако, как указано выше, это недетерминировано). И когда это произойдет, это будет main() горутин, и так, когда main() заканчивается и возвращается, ваше приложение завершается.

Игра со своим приложением на игровой площадке Go:

Как уже упоминалось, по умолчанию GOMAXPROCS является 1 на игровой площадке Go. Однако разрешено устанавливать его на более высокое значение, например:

runtime.GOMAXPROCS(2)

Без явной синхронизации выполнение все еще остается недетерминированным, однако вы будете наблюдать другой порядок выполнения и завершение без превышения времени ожидания:

Hello, playground
Here
Here
Here
...
<Here is printed 996 times, then:>
Finished.

Попробуйте этот вариант на игровой площадке Go.

То, что вы увидите на экране, недетерминировано. Точнее, если случайно true Значение, которое вы передаете каналу, задерживается, вы увидите некоторое "Здесь".

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

Практическое правило гласит: главная функция должна быть жива, в противном случае все другие функции будут убиты.

Другие вопросы по тегам