链式函数如何作为goroutine执行?

huangapple go评论86阅读模式
英文:

How do chained functions get executed as goroutines?

问题

给定这个 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")
}

为什么输出是:

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

anotherFunconeFunc返回值之前不会执行。因此,在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 &quot;fmt&quot;

func main() {
	ch := make(chan int)
	go oneFunc(ch).anotherFunc()
	fmt.Println(&quot;Waiting...&quot;)
	&lt;-ch
	fmt.Println(&quot;Done.&quot;)
}

func oneFunc(ch chan int) something {
	fmt.Println(&quot;oneFunc&quot;)
	return something{ch}
}

type something struct{
	ch chan int
}

func (s something) anotherFunc() {
	fmt.Println(&quot;anotherFunc&quot;)
	s.ch &lt;- 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(&quot;oneFunc&quot;)
	
	buf := make([]byte, 4096)
	runtime.Stack(buf, false)
	fmt.Println(&quot;Stack of oneFunc:&quot;, string(buf))
	
	return something{}
}

type something struct{}

func (s something) anotherFunc() {
	defer wg.Done()
	
	buf := make([]byte, 4096)
	runtime.Stack(buf, false)
	fmt.Println(&quot;Stack of anotherFunc:&quot;, string(buf))
	
	fmt.Println(&quot;anotherFunc&quot;)
}

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 &ndash; like defer &ndash; 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

huangapple
  • 本文由 发表于 2013年11月6日 06:34:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/19800426.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定