Go 例程是 golang 的一个很好的卖点,使其成为许多开发人员的选择。在这篇文章中,我们将看到这些goroutine 的一个常见问题并尝试解决它。
让我们看一个简单的代码片段来说明这个问题,
Go
package main
import "fmt"
func runner1() {
fmt.Print("\nI am first runner")
}
func runner2() {
fmt.Print("\nI am second runner")
}
func execute() {
go runner1()
go runner2()
}
func main() {
// Launching both the runners
execute()
}
Go
package main
import (
"fmt"
"time"
)
func runner1() {
fmt.Print("\nI am first runner")
}
func runner2() {
fmt.Print("\nI am second runner")
}
func execute() {
go runner1()
go runner2()
}
func main() {
// Launching both the runners
execute()
time.Sleep(time.Second)
}
Go
package main
import (
"fmt"
"sync"
)
func runner1(wg *sync.WaitGroup) {
defer wg.Done() // This decreases counter by 1
fmt.Print("\nI am first runner")
}
func runner2(wg *sync.WaitGroup) {
defer wg.Done()
fmt.Print("\nI am second runner")
}
func execute() {
wg := new(sync.WaitGroup)
wg.Add(2)
// We are increasing the counter by 2
// because we have 2 goroutines
go runner1(wg)
go runner2(wg)
// This Blocks the execution
// until its counter become 0
wg.Wait()
}
func main() {
// Launching both the runners
execute()
}
正如您刚刚看到的那样,输出中没有任何内容,这是因为一旦您启动了两个 goroutine,您的 main函数终止。 Golang 中的每个程序都会执行,直到 main函数没有终止。那么,对于这个问题,我们能做些什么
1.我们可以在运行 runners 后等待一段时间,为此我们将使用“ time ”包函数“ Sleep ”在给定的时间内暂停函数的执行,
去
package main
import (
"fmt"
"time"
)
func runner1() {
fmt.Print("\nI am first runner")
}
func runner2() {
fmt.Print("\nI am second runner")
}
func execute() {
go runner1()
go runner2()
}
func main() {
// Launching both the runners
execute()
time.Sleep(time.Second)
}
输出:
I am second runner
I am first runner
我们只是解决了这个问题,在启动我们的跑步者后我们等待一秒钟,所以我们的主要函数是睡眠(阻塞)1 秒。在这段时间内,所有的 go 例程都成功执行了。但是 Golang 是一种快速的语言,只需打印 2 个字符串就不需要 1 秒。
问题是,我们的执行程序执行时间很短,因此我们不必要地将程序阻塞了 1 秒。在这个例子中,这似乎不是一个关键问题,但如果你制作一个生产级服务器,它将同时为 1000 个请求提供服务,这将是一个大问题。
2.让我们使用另一个 Golang 的标准库原语“ sync.WaitGroup ”。 WaitGroup实际上是一种计数器,它阻止函数的执行(或者可以说是一个 goroutine),直到其内部计数器变为 0 。
这个怎么运作 ?
WaitGroup 导出 3 个方法。
1 | Add(int) | It increases WaitGroup counter by given integer value. |
2 | Done() | It decreases WaitGroup counter by 1, we will use it to indicate termination of a goroutine. |
3 | Wait() | It Blocks the execution until it’s internal counter becomes 0. |
注意:WaitGroup 是并发安全的,因此将指向它的指针作为 Groutine 的参数传递是安全的。
去
package main
import (
"fmt"
"sync"
)
func runner1(wg *sync.WaitGroup) {
defer wg.Done() // This decreases counter by 1
fmt.Print("\nI am first runner")
}
func runner2(wg *sync.WaitGroup) {
defer wg.Done()
fmt.Print("\nI am second runner")
}
func execute() {
wg := new(sync.WaitGroup)
wg.Add(2)
// We are increasing the counter by 2
// because we have 2 goroutines
go runner1(wg)
go runner2(wg)
// This Blocks the execution
// until its counter become 0
wg.Wait()
}
func main() {
// Launching both the runners
execute()
}
输出:
I am second runner
I am first runner
输出相同,但我们的程序不会阻塞 1 秒。模式,我们上面向您展示的是在 Golang 中编写并发代码的常见做法。