Добро пожаловать в переполнение стека.Я надеюсь, что смогу помочь вам немного лучше понять каналы и функции.
Концепция 1: Каналы
Визуализируйте канал как трубу, куда данные выходят с одного конца и наружудругой.Первые данные в это первые данные, которые выходят на другую сторону.Есть буферизованные каналы и небуферизованные каналы, но для вашего примера вам нужно только понять канал по умолчанию, который не буферизован.Небуферизованные каналы допускают только одно значение в канале за раз.
Запись в небуферизованный канал
Код, который выглядит так, записывает данные в один конец канала.
ch <- value
Теперь этот код на самом деле ожидает выполнения до тех пор, пока что-то не прочитает значение из канала.Небуферизованный канал допускает только одно значение за раз, чтобы быть в нем, и не продолжает выполняться, пока не будет прочитано.Позже мы увидим, как это влияет на порядок выполнения вашего кода.
Чтение с небуферизованного канала
Чтение с небуферизованного канала (визуализируйте удаление значения из канала)код для этого выглядит как
[value :=] <-ch
, когда вы читаете документацию по коду [вещи в] квадратных скобках указывают, что то, что внутри них, является необязательным.Выше, без [value: =] вы просто извлекаете значение из канала и не используете его ни для чего.
Теперь, когда в канале есть значение, этот код имеетдва побочных эффекта.Во-первых, он считывает значение из канала в любой подпрограмме, в которой мы сейчас находимся, и переходит к значению. другой эффект, который он имеет, состоит в том, чтобы позволил программе, которая помещает значение в канал, продолжить .Это критический бит, необходимый для понимания вашей программы-примера.
Если в канале еще нет значения NO, он будет ждать записи значения в канал, прежде чем продолжить .Другими словами, поток блокируется до тех пор, пока канал не получит значение для чтения.
Программа позволяет вашему коду продолжать выполнение двух частей кода. одновременно .Это может быть использовано для ускорения выполнения вашего кода или одновременного решения нескольких проблем (представьте себе сервер, на который несколько пользователей одновременно загружают страницы с него).
Ваш вопрос возникает, когдавы пытаетесь выяснить порядок выполнения кода, если у вас есть несколько подпрограмм, выполняющихся одновременно.Это хороший вопрос, и другие правильно заявили, что это зависит .Когда вы создаете две процедуры, порядок выполнения строк кода является произвольным.
Код, указанный ниже, вместе с программой может сначала вывести executing a()
или end main()
.Это связано с тем, что порождение группирования означает, что одновременно происходит два потока (потока) выполнения.В этом случае один поток остается в main()
, а другой начинает выполнение первой строки в a()
.То, как среда выполнения решает выбрать, какой запускать первым, является произвольным.
func main() {
fmt.Println("start main()")
go a()
fmt.Println("end main()")
}
func a() {
fmt.Println("executing a()")
}
Goroutines + Channels
Теперь давайте воспользуемся каналом, чтобы контролировать порядок выполнения, когда выполняется get.Единственная разница теперь в том, что мы создаем канал, передаем его в процедуру и ждем, пока его значение будет записано, прежде чем продолжить в main.Ранее мы обсуждали, как процедура чтения значения из канала должна ждать, пока в канале не появится значение, прежде чем продолжить.Поскольку executing a()
всегда печатается перед записью канала, мы всегда будем ждать чтения значения, введенного в канал, до тех пор, пока не будет напечатано executing a()
.Поскольку мы читаем с канала (что происходит после записи канала) перед печатью end main()
, executing a()
всегда будет печатать до end main()
.Я создал эту игровую площадку , чтобы вы могли сами ее запустить.
func main() {
fmt.Println("start main()")
ch := make(chan int)
go a(ch)
<-ch
fmt.Println("end main()")
}
func a(ch chan int) {
fmt.Println("executing a()")
ch <- 0
}
Ваш пример
Я думаю, что в этот момент вы могли бы выяснить, что происходит, когда и что может происходить в другом порядке.Моя первая попытка была неудачной, когда я прошел через это в своей голове (см. Историю изменений).Ты должен быть осторожен!Я не дам правильного ответа после редактирования, так как понял, что это может быть домашнее задание.
РЕДАКТИРОВАТЬ: больше семантики о <-done
На моемВначале я забыл упомянуть, что fmt.Println(<-done)
концептуально совпадает со следующим:
value := <-done
fmt.Println(value)
Это важно, потому что это помогает увидеть, когда поток main()
читает из done
канал, он не печатает его одновременно.Это два отдельных этапа выполнения.
Надеюсь, это поможет.Дайте мне знать, если у вас есть вопросы.