当SayHello()
按预期执行时,goroutine不会打印任何内容。
package main
import "fmt"
func SayHello() {
for i := 0; i < 10 ; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
go SayHello()
}
答案 0 :(得分:31)
当您的main()
功能结束时,您的计划也会结束。它不会等待其他goroutines完成。
引用Go Language Specification: Program Execution:
程序执行从初始化主包然后调用函数
main
开始。当该函数调用返回时,程序退出。它不会等待其他(非main
)goroutines完成。
有关详细信息,请参阅this answer。
你必须告诉你的main()
函数等待SayHello()
函数作为goroutine完成。您可以将它们与频道同步,例如:
func SayHello(done chan int) {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
if done != nil {
done <- 0 // Signal that we're done
}
}
func main() {
SayHello(nil) // Passing nil: we don't want notification here
done := make(chan int)
go SayHello(done)
<-done // Wait until done signal arrives
}
另一种方法是通过关闭频道来表示完成:
func SayHello(done chan struct{}) {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
if done != nil {
close(done) // Signal that we're done
}
}
func main() {
SayHello(nil) // Passing nil: we don't want notification here
done := make(chan struct{})
go SayHello(done)
<-done // A receive from a closed channel returns the zero value immediately
}
备注:强>
根据您的编辑/评论:如果您希望2个正在运行的SayHello()
功能打印&#34;混合&#34;数字随机:你无法保证观察到这种行为。再次,请参阅aforementioned answer了解更多详情。 Go Memory Model仅保证某些事件在其他事件发生之前发生,您无法保证如何执行2个并发的goroutine。
您可以尝试使用它,但要知道结果不是确定性的。首先,您必须使用以下命令启用多个活动goroutine:
runtime.GOMAXPROCS(2)
第二,你必须首先启动SayHello()
作为goroutine,因为你当前的代码首先在主goroutine中执行SayHello()
,只有在完成后才启动另一个:
runtime.GOMAXPROCS(2)
done := make(chan struct{})
go SayHello(done) // FIRST START goroutine
SayHello(nil) // And then call SayHello() in the main goroutine
<-done // Wait for completion
答案 1 :(得分:10)
或者(对于icza的回答),您可以使用WaitGroup
包和匿名函数中的sync
来避免更改原始SayHello
。
package main
import (
"fmt"
"sync"
)
func SayHello() {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
SayHello()
}()
wg.Wait()
}
为了同时打印数字,请在单独的例程中运行每个print语句,如下所示
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(fnScopeI int) {
defer wg.Done()
// next two strings are here just to show routines work simultaneously
amt := time.Duration(rand.Intn(250))
time.Sleep(time.Millisecond * amt)
fmt.Print(fnScopeI, " ")
}(i)
}
wg.Wait()
}
答案 2 :(得分:0)
就像其他人提到的那样,当main
函数返回时,Go程序退出。
一种选择是使用sync.WaitGroup
之类的东西来等待main
产生的其他goroutine,然后再从main
返回。
另一种选择是在runtime.Goexit()
中调用main
。来自godoc:
Goexit终止调用它的goroutine。没有其他goroutine受到影响。 Goexit在终止goroutine之前运行所有延迟的调用。由于Goexit并非恐慌,因此这些延迟函数中的任何恢复调用都将返回nil。
从主goroutine调用Goexit会终止该goroutine,而不会返回func main。由于func main尚未返回,因此程序将继续执行其他goroutine。如果所有其他goroutine退出,程序将崩溃。
这允许主goroutine在后台例程继续执行时停止执行。例如:
package main
import (
"fmt"
"runtime"
"time"
)
func f() {
for i := 0; ; i++ {
fmt.Println(i)
time.Sleep(10 * time.Millisecond)
}
}
func main() {
go f()
runtime.Goexit()
}
这比永久阻塞主函数更干净,尤其是对于无限大的程序。缺点是,如果某个流程的所有goroutine都返回或退出(包括主goroutine),则Go会将其检测为错误并感到恐慌:
fatal error: no goroutines (main called runtime.Goexit) - deadlock!
为避免这种情况,至少一个goroutine必须在返回之前调用os.Exit
。调用os.Exit(0)
会立即终止程序,并指示它这样做没有错误。例如:
package main
import (
"fmt"
"os"
"runtime"
"time"
)
func f() {
for i := 0; i < 10; i++ {
fmt.Println(i)
time.Sleep(10 * time.Millisecond)
}
os.Exit(0)
}
func main() {
go f()
runtime.Goexit()
}