英文:
How do chained functions get executed as goroutines?
问题
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" 从未被打印出来?
英文:
Given this playground:
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")
}
Why is the output:
> oneFunc
and "anotherFunc" is never printed?
答案1
得分: 5
anotherFunc
在oneFunc
返回值之前不会执行。因此,在anotherFunc
能够运行之前,程序就已经退出了。你需要在main
退出之前等待anotherFunc
运行。
你可以通过Go通道来实现这一点。例如:
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
}
希望对你有帮助!
英文:
anotherFunc
doesn't execute until oneFunc
returns a value. Because of this, the program exits before anotherFunc
is able to run. You need to wait for anotherFunc
to run before main
exits.
You can do this via Go channels. For Example:
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
}
答案2
得分: 2
go
关键字后面的表达式会被求值,并且该表达式的函数值会被并发执行。
所以,在你的例子中,oneFunc()
被调用,因此会输出 oneFunc
,然后在返回的实例上并发调用方法 anotherFunc
。
然而,在 goroutine 运行之前,你的程序就已经终止了,这就是为什么你看不到 anotherFunc
被打印出来。
解决方案:使用 sync.WaitGroup
或通道进行同步。
为了实际(经验性地)验证你的 go
调用是否并发执行 anotherFunc
而不是 oneFunc
,你可以在每个函数中打印堆栈并比较输出。以下是一个示例(在 play 上):
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("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("anotherFunc 的堆栈:", string(buf))
fmt.Println("anotherFunc")
}
你会看到类似以下的输出:
oneFunc
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
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
堆栈跟踪甚至告诉你这两个函数在不同的 goroutine 中运行,不需要比较方法调用。
英文:
The expression behind the go
keyword is evaluated and the function value of that expression is then executed concurrently.
So, in your example oneFunc()
is called, hence the oneFunc
output, and the method anotherFunc
on the returned instance is called concurrently.
However, your program terminates before the goroutine can run which
is why you don't see anotherFunc
printed.
Solution: Use sync.WaitGroup
or channels for synchronization.
To actually (empirically) verify that your go
call executes anotherFunc
concurrently
and not oneFunc
you can print the stack in each function and compare the output.
Example (on play):
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")
}
You will see something like this:
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
The stack trace even tells you that the two functions are running in different
goroutines, no comparison of method calls needed.
答案3
得分: 2
我喜欢这样解释,go
- 就像 defer
一样 - 使用行中的最后一个调用或一对括号,并且会无序地调用该函数。在此之前的每个调用都是同步的。
go
使调用并发执行。而 defer
则延迟调用,直到当前函数返回。
在《Effective Go》的defer
部分中有一个非常好的例子。
英文:
The way that I like to about it is this, go
– like defer
– consumes the last invocation, or pair parentheses, on the line and will invoke that function out of order. Every invocation before that is synchronous.
Where go
makes the invocation concurrent. And defer
delays invocation until current function returns.
There is a really good example of this in the defer
section of Effective Go
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论