多个 goroutine 打印内容

原创
2013/06/18 16:35
阅读数 897

在使用多个 goroutine 打印内容时,经常因为使用 chan 不恰当而 导致主线程未等待其它 goroutine 全部执行完毕而匆匆推出,造成打印内容不全的问题,这里对其中一种情况进行讲解。

首先是新手易犯错的代码(测试环境 Windows 7 x64,Go 1.1.1,下同):

package main

import (
        "fmt"
        "runtime"
)

// 从 1 至 1 亿循环叠加,并打印结果。
func print(c chan bool, n int) {
        x := 0
        for i := 1; i <= 100000000; i++ {
                x += i
        }

        fmt.Println(n, x)

        if n == 9 {
                c <- true
        }
}

func main() {
        // 使用多核运行程序
        runtime.GOMAXPROCS(runtime.NumCPU())
        c := make(chan bool)

        for i := 0; i < 10; i++ {
                go print(c, i)
        }

        <-c

        fmt.Println("DONE.")
}


这段代码从逻辑上看合乎情理,但是是一种非常 投机取巧 的做法,即根据第 10 个 goroutine 的执行情况来 草率地 认为前面的 9 个 goroutine 都已经执行完毕。如果你将 `runtime.GOMAXPROCS(runtime.NumCPU())` 这句注释掉,使用单核运行程序,则将得到你所预期的效果;但如果使用多核的情况下,这种做法就是 错误的。goroutine 是相互独立的,且在执行过程中可能由于各种原因导致其中几个 goroutine 让出时间片给 CPU 去执行其它 goroutine。所以,我们 不能够依靠 第 10 个 goroutine 的执行结果来判断程序的运行情况。

解决方案一: 利用 chan 的缓存机制
package main

import (
        "fmt"
        "runtime"
)

// 从 1 至 1 亿循环叠加,并打印结果。
func print(c chan bool, n int) {
        x := 0
        for i := 1; i <= 100000000; i++ {
                x += i
        }

        fmt.Println(n, x)

        c <- true
}

func main() {
    // 使用多核运行程序
        runtime.GOMAXPROCS(runtime.NumCPU())
        c := make(chan bool, 10)

        for i := 0; i < 10; i++ {
                go print(c, i)
        }

        for i := 0; i < 10; i++ {
                <-c
        }

        fmt.Println("DONE.")
}

通过创建一个最大可容纳 10 个对象的 chan 来确保一种 极端情况,即 10 个 goroutine 同时完成打印并向 chan 写入内容。然后,我们通过在 main 函数中进行 10 次读取操作,来确保 chan 中的对象确实被存进去了 10 个,从而保证所有的 goroutine 都执行完毕,然后退出程序。

解决方案二:使用 sync 包的 WaitGroup

package main

import (
        "fmt"
        "runtime"
        "sync"
)

// 从 1 至 1 亿循环叠加,并打印结果。
func print(wg *sync.WaitGroup, n int) {
        x := 0
        for i := 1; i <= 100000000; i++ {
                x += i
        }

        fmt.Println(n, x)
        // 标识一次任务完成
        wg.Done()
}

func main() {
        // 使用多核运行程序
        runtime.GOMAXPROCS(runtime.NumCPU())
        // 创建等待组
        wg := sync.WaitGroup{}
        // 设置需要等待的对象个数
        wg.Add(10)

        for i := 0; i < 10; i++ {
                go print(&wg, i)
        }

        // 等待所有任务完成
        wg.Wait()

        fmt.Println("DONE.")
}
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
3 收藏
0
分享
返回顶部
顶部