Как работает ключевое слово go
в Go, см. Go_statements :
Значение функции и Параметры равны оценивается как обычно в вызывающей программе, но в отличие от обычного вызова, выполнение программы не ожидает завершения вызванной функции. Вместо этого функция начинает выполняться независимо в новой процедуре. Когда функция завершается, ее процедура также завершается. Если у функции есть какие-либо возвращаемые значения, они сбрасываются после завершения функции.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 * * * *. на месте с ключевым словом go
(то же самое для ключевого слова defer
, см. Пример для ключевого слова defer
).
Чтобы понять порядок оценки,давайте попробуем это:
go have()(fun("with Go."))
Давайте запустим this и прочитаем комментарии к коду для порядка оценки:
package main
import (
"fmt"
"sync"
)
func main() {
go have()(fun("with Go."))
fmt.Print("some ") // evaluation order: ~ 3
wg.Wait()
}
func have() func(string) {
fmt.Print("Go ") // evaluation order: 1
return funWithGo
}
func fun(msg string) string {
fmt.Print("have ") // evaluation order: 2
return msg
}
func funWithGo(msg string) {
fmt.Println("fun", msg) // evaluation order: 4
wg.Done()
}
func init() {
wg.Add(1)
}
var wg sync.WaitGroup
Вывод:
Go have some fun with Go.
Пояснение go have()(fun("with Go."))
:
Здесь выполняется первая оценка на месте:
go have()(...)
первая have()
обработка детали и результат fmt.Print("Go ")
и return funWithGo
, затем fun("with Go.")
работает, и результат fmt.Print("have ")
и return "with Go."
;теперь у нас есть go funWithGo("with Go.")
.
Итак, последний вызов программы - это go funWithGo("with Go.")
Это вызов для запуска новой программы, поэтому на самом деле мы не знаем, когда она запустится. Так что есть шанс для следующей строки: fmt.Print("some ")
, тогда мы ждем здесь wg.Wait()
. Теперь программа запускает это funWithGo("with Go.")
, и результат будет fmt.Println("fun", "with Go.")
затем wg.Done()
;это все.
Давайте перепишем приведенный выше код, просто заменим именованные функции на анонимные, так что этот код такой же, как указано выше:
Например, см .:
func have() func(string) {
fmt.Print("Go ") // evaluation order: 1
return funWithGo
}
И вырезатьэтот код выберите have
часть в go have()
и вставьте, затем выберите have
часть в func have()
и нажмите Delete
на клавиатуре, тогда у вас будет это:
This еще красивее, с тем же результатом, просто замените все функции анонимными функциями:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() func(string) {
fmt.Print("Go ") // evaluation order: 1
return func(msg string) {
fmt.Println("fun", msg) // evaluation order: 4
wg.Done()
}
}()(func(msg string) string {
fmt.Print("have ") // evaluation order: 2
return msg
}("with Go."))
fmt.Print("some ") // evaluation order: ~ 3
wg.Wait()
}
Позвольте мне объяснить это на простом примере:
1. Рассмотрим этот простой код:
i := 1
go fmt.Println(i) // 1
Это достаточно ясно: вывод 1
.
Но если дизайнеры Go решили оценить аргумент функции на время выполнения функции , то никто не знает значение i
;Вы можете изменить i
в своем коде (см. следующий пример)
Теперь давайте сделаем это замыкание:
i := 1
go func() {
time.Sleep(1 * time.Second)
fmt.Println(i) // ?
}()
На самом деле вывод неизвестен , и если программа main
завершится раньше, у нее даже не будетшанс на бег: проснись и напечатай i
, что само по себе i
может измениться к этому конкретному моменту.
Теперь давайте решим это так:
i := 1
go func(i int) {
fmt.Printf("Step 3 i is: %d\n", i) // i = 1
}(i)
Этот аргумент анонимной функции имеет тип int
, это тип значения, а значение i
известно, и сгенерированный компилятором код помещает значение 1
(i
) в стек, поэтому эта функция будет использовать значение 1
, когда придет время (время в будущем).
Все (
Игровая площадка Go ):
package main
import (
"fmt"
"sync"
"time"
)
func main() {
i := 1
go fmt.Println(i) // 1 (when = unknown)
go fmt.Println(2) // 2 (when = unknown)
go func() { // closure
time.Sleep(1 * time.Second)
fmt.Println(" This won't have a chance to run", i) // i = unknown (when = unknown)
}()
i = 3
wg := new(sync.WaitGroup)
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("Step 3 i is: %d\n", i) // i = 3 (when = unknown)
}(i)
i = 4
go func(step int) { // closure
fmt.Println(step, i) // i=? (when = unknown)
}(5)
i = 5
fmt.Println(i) // i=5
wg.Wait()
}
Вывод:
5
5 5
2
1
Step 3 i is: 3
Вывод Go Playground:
5
5 5
1
2
Step 3 i is: 3
Как вы можете заметить, порядок 1
и 2
является случайным, и ваш вывод может отличаться (см. Комментарии к коду).