防止 main() 函数在 Golang 中的 goroutines 完成之前终止
Prevent the main() function from terminating before goroutines finish in Golang
看看这个人为的例子:
package main
import "fmt"
func printElo() {
fmt.Printf("Elo\n")
}
func printHello() {
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
go printElo()
go printHello()
i++
}
}
这个程序的输出只是 "This will print"。 goroutines printElo()
和 printHello
的输出将不会发出,因为我猜 main()
函数线程将在 goroutines 有机会开始执行之前完成。
使类似代码在 Golang 中工作并且不会过早终止的惯用方法是什么?
最简单、最干净和“可扩展”的方法是使用 sync.WaitGroup
:
var wg = &sync.WaitGroup{}
func printElo() {
defer wg.Done()
fmt.Printf("Elo\n")
}
func printHello() {
defer wg.Done()
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
wg.Add(1)
go printElo()
wg.Add(1)
go printHello()
i++
}
wg.Wait()
}
输出(在 Go Playground 上尝试):
This will print.Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
使用 sync.WaitGroup
时要遵循的简单“规则”:
- 调用
WaitGroup.Add()
in the "original" goroutine (that starts a new) before the go
语句
- 建议延迟调用
WaitGroup.Done()
,这样即使 goroutine 出现 panic 也会被调用
- 如果你想将
WaitGroup
传递给其他函数(而不是使用包级变量),你必须传递一个指向它的指针,否则 WaitGroup
(它是一个结构)会被复制,在副本上调用的 Done()
方法不会在原始 上观察到
如果您只想玩玩结果,您可以使用 "hack" 等待输入:
package main
import (
"fmt"
"bufio"
"os"
)
func printElo() {
fmt.Printf("Elo\n")
}
func printHello() {
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
go printElo()
go printHello()
i++
}
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
}
如果想了解如何进行同步,请阅读同步包:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func printElo() {
fmt.Printf("Elo\n")
wg.Done()
}
func printHello() {
fmt.Printf("Hello\n")
wg.Done()
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
wg.Add(2)
go printElo()
go printHello()
i++
}
wg.Wait()
}
您可以使用sync package and take a look at waitgroups
. You can take a look at a working Goplayground I set up.
基本上
package main
import (
"fmt"
"sync"
)
//Takes a reference to the wg and sleeps when work is done
func printElo(wg *sync.WaitGroup) {
fmt.Printf("Elo\n")
defer wg.Done()
}
//Takes a reference to the wg and sleeps when work is done
func printHello(wg *sync.WaitGroup) {
fmt.Printf("Hello\n")
defer wg.Done()
}
func main() {
//Create a new WaitGroup
var wg sync.WaitGroup
fmt.Println("This will print.")
for i := 0; i < 10; i++ {
//Add a new entry to the waitgroup
wg.Add(1)
//New Goroutine which takes a reference to the wg
go printHello(&wg)
//Add a new entry to the waitgroup
wg.Add(1)
//New Goroutine which takes a reference to the wg
go printElo(&wg)
}
//Wait until everything is done
wg.Wait()
}
如前所述,sync.WaitGroup
是生产代码中的正确方法。但是当开发用于测试和调试目的时,您可以在末尾添加 select{}
语句或 main()
.
func main(){
go routine()
...
select{}
}
main()
然后永远不会 returns 你会用例如 Ctrl-C
杀死它。它不是惯用的,从未在生产中使用过,但在开发时非常快速简单。
看看这个人为的例子:
package main
import "fmt"
func printElo() {
fmt.Printf("Elo\n")
}
func printHello() {
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
go printElo()
go printHello()
i++
}
}
这个程序的输出只是 "This will print"。 goroutines printElo()
和 printHello
的输出将不会发出,因为我猜 main()
函数线程将在 goroutines 有机会开始执行之前完成。
使类似代码在 Golang 中工作并且不会过早终止的惯用方法是什么?
最简单、最干净和“可扩展”的方法是使用 sync.WaitGroup
:
var wg = &sync.WaitGroup{}
func printElo() {
defer wg.Done()
fmt.Printf("Elo\n")
}
func printHello() {
defer wg.Done()
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
wg.Add(1)
go printElo()
wg.Add(1)
go printHello()
i++
}
wg.Wait()
}
输出(在 Go Playground 上尝试):
This will print.Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
使用 sync.WaitGroup
时要遵循的简单“规则”:
- 调用
WaitGroup.Add()
in the "original" goroutine (that starts a new) before thego
语句 - 建议延迟调用
WaitGroup.Done()
,这样即使 goroutine 出现 panic 也会被调用 - 如果你想将
WaitGroup
传递给其他函数(而不是使用包级变量),你必须传递一个指向它的指针,否则WaitGroup
(它是一个结构)会被复制,在副本上调用的Done()
方法不会在原始 上观察到
如果您只想玩玩结果,您可以使用 "hack" 等待输入:
package main
import (
"fmt"
"bufio"
"os"
)
func printElo() {
fmt.Printf("Elo\n")
}
func printHello() {
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
go printElo()
go printHello()
i++
}
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
}
如果想了解如何进行同步,请阅读同步包:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func printElo() {
fmt.Printf("Elo\n")
wg.Done()
}
func printHello() {
fmt.Printf("Hello\n")
wg.Done()
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
wg.Add(2)
go printElo()
go printHello()
i++
}
wg.Wait()
}
您可以使用sync package and take a look at waitgroups
. You can take a look at a working Goplayground I set up.
基本上
package main
import (
"fmt"
"sync"
)
//Takes a reference to the wg and sleeps when work is done
func printElo(wg *sync.WaitGroup) {
fmt.Printf("Elo\n")
defer wg.Done()
}
//Takes a reference to the wg and sleeps when work is done
func printHello(wg *sync.WaitGroup) {
fmt.Printf("Hello\n")
defer wg.Done()
}
func main() {
//Create a new WaitGroup
var wg sync.WaitGroup
fmt.Println("This will print.")
for i := 0; i < 10; i++ {
//Add a new entry to the waitgroup
wg.Add(1)
//New Goroutine which takes a reference to the wg
go printHello(&wg)
//Add a new entry to the waitgroup
wg.Add(1)
//New Goroutine which takes a reference to the wg
go printElo(&wg)
}
//Wait until everything is done
wg.Wait()
}
如前所述,sync.WaitGroup
是生产代码中的正确方法。但是当开发用于测试和调试目的时,您可以在末尾添加 select{}
语句或 main()
.
func main(){
go routine()
...
select{}
}
main()
然后永远不会 returns 你会用例如 Ctrl-C
杀死它。它不是惯用的,从未在生产中使用过,但在开发时非常快速简单。