多个 goroutine 打印内容

在使用多个 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.")
}

转载于:https://my.oschina.net/Obahua/blog/138428

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值