Как связанные функции выполняются как горутины?

Учитывая эту игровую площадку:

package main

import "fmt"

func main() {
    go oneFunc().anotherFunc()
}

func oneFunc() something {
    fmt.Println("oneFunc")
    return something{}
}

type something struct{}

func (s something) anotherFunc() {
    fmt.Println("anotherFunc")
}

Почему вывод:

oneFunc

а "anotherFunc" никогда не печатается?


person Matt    schedule 05.11.2013    source источник


Ответы (3)


anotherFunc не выполняется, пока oneFunc не вернет значение. Из-за этого программа завершает работу до того, как anotherFunc сможет запуститься. Вам нужно дождаться запуска anotherFunc перед выходом main.

Вы можете сделать это через каналы Go. Например:

http://play.golang.org/p/dWoLB9afSj

package main

import "fmt"

func main() {
    ch := make(chan int)
    go oneFunc(ch).anotherFunc()
    fmt.Println("Waiting...")
    <-ch
    fmt.Println("Done.")
}

func oneFunc(ch chan int) something {
    fmt.Println("oneFunc")
    return something{ch}
}

type something struct{
    ch chan int
}

func (s something) anotherFunc() {
    fmt.Println("anotherFunc")
    s.ch <- 1
}
person Brenden    schedule 05.11.2013

Мне нравится в этом следующий способ: go как defer потребляет последний вызов или пару круглых скобок в строке и вызывает эту функцию не по порядку. Каждый вызов до этого является синхронным.

Где go делает вызов параллельным. А defer откладывает вызов до возврата текущей функции.

В разделе defer страницы Эффективный переход

person J. Holmes    schedule 05.11.2013
comment
Хотя вы поделились очень полезной ссылкой на Effective Go, ваш ответ не дает объяснения поведения, описанного в вопросе. - person maxbublis; 26.09.2014

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

Итак, в вашем примере вызывается oneFunc(), следовательно, вывод oneFunc, а метод anotherFunc для возвращаемого экземпляра вызывается одновременно. Однако ваша программа завершается до того, как горутина может быть запущена, поэтому вы не видите anotherFunc напечатанным.

Решение: используйте sync.WaitGroup или каналы для синхронизации.

Чтобы на самом деле (эмпирически) убедиться, что ваш вызов go выполняется anotherFunc одновременно, а нет oneFunc, вы можете распечатать стек в каждой функции и сравнить результат. Пример (в игре):

var wg = sync.WaitGroup{}

func main() {
    wg.Add(1)
    go oneFunc().anotherFunc()
    wg.Wait()
}

func oneFunc() something {
    fmt.Println("oneFunc")

    buf := make([]byte, 4096)
    runtime.Stack(buf, false)
    fmt.Println("Stack of oneFunc:", string(buf))

    return something{}
}

type something struct{}

func (s something) anotherFunc() {
    defer wg.Done()

    buf := make([]byte, 4096)
    runtime.Stack(buf, false)
    fmt.Println("Stack of anotherFunc:", string(buf))

    fmt.Println("anotherFunc")
}

Вы увидите что-то вроде этого:

oneFunc
Stack of oneFunc: goroutine 1 [running]:
main.oneFunc()
    /tmpfs/gosandbox-342f581d_b6e8aa8b_334a0f88_c8221b7e_20882985/prog.go:20 +0x118
main.main()
    /tmpfs/gosandbox-342f581d_b6e8aa8b_334a0f88_c8221b7e_20882985/prog.go:11 +0x50

Stack of anotherFunc: goroutine 2 [running]:
main.something.anotherFunc()
    /tmpfs/gosandbox-342f581d_b6e8aa8b_334a0f88_c8221b7e_20882985/prog.go:32 +0xb2
created by main.main
    /tmpfs/gosandbox-342f581d_b6e8aa8b_334a0f88_c8221b7e_20882985/prog.go:11 +0x69

anotherFunc

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

person nemo    schedule 05.11.2013