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,主函数便会终止。 Golang中的每个程序都会执行,直到主函数没有终止。那么,我们该怎么办
1.我们可以在启动跑步者之后等待一段时间,为此,我们将使用“时间”包函数“睡眠”,该函数在给定持续时间内暂停执行该功能,
去
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秒钟。在此期间,所有执行例程均已成功执行。但是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是并发安全的,因此可以安全地将指向它的指针作为Groutines的参数传递。
去
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
输出是相同的,但是我们out程序在1秒钟内没有阻塞。上面我们向您展示的模式是在Golang中编写并发代码的一种常见做法。