Go圣经-学习笔记之并发循环

上一篇 Go圣经-学习笔记之error接口golang

下一篇 Go圣经-学习笔记之select多路复用并发

首先说一个,原本上一篇文章应该是, Go圣经-学习笔记之Channel和Goroutine。写了很长一段,结果被有道云笔记TMD坑惨了,服务掉了,不能拷贝,不能保存。这篇文章就丢了。若是之后有时间,我再补,最近比较忙,先把后面部分写完。函数

并发循环

func main() {
    var count = 10
    var mg = make(chan struct{}, count)
    for i := 0; i < count; i++ {
        go func() {
            fmt.Printf("i=%d\n", i+1)
            mg <- struct{}{}
        }()
    }
    for i := 0; i < count; i++ {
        <-mg
    }
    close(mg)
    return
}

在Go圣经-学习笔记之函数值那篇文章中,能够知道函数值是由函数体和引用环境构成的。因此上面的返回结果是在GoSched调用Goroutine后运行时的引用变量,因此这个打印值是不肯定的,可是通常来讲结果是11。在之后写雨痕老师的学习笔记时,我再来具体讲解。Go f()只是建立一个goroutine,只有Goroutine被GoSched调度后才能真实的运行在内核态,这个时候获取变量i时,就是这时内存i变量值。这里for循环运行结束后,10个goroutine还没来得及被调度,因此i值都是11。学习

func main() {
    var count = 10
    var mg = make(chan struct{}, count)
    for i := 0; i < count; i++ {
        go func() {
            fmt.Printf("i=%d\n", i+1)
            mg <- struct{}{}
        }()
        time.Sleep(3 * time.Second)  // main goroutine sleep 3s
    }
    for i := 0; i < count; i++ {
        <-mg
    }
    close(mg)
    return
}

每执行一次for循环,main goroutine休息3s,则其余goroutine确定会被GoSched调度,因此这个时候,打印值分别是:1,2,3,4,..., 10.net

改进一

若是你不想把变量i放在引用环境中,另外一种方法code

func main() {
    var count = 10
    var mg = make(chan struct{}, count)
    for i := 0; i < count; i++ {
        go func(i int) {
            fmt.Printf("i=%d\n", i+1)
            mg <- struct{}{}
        }(i)
    }
    for i := 0; i < count; i++ {
        <-mg
    }
    close(mg)
    return
}

改进二

若是goroutine执行过程当中发生了错误,须要把错误信息汇总到一个指定的goroutine,能够使用channel来实现。blog

func main() {
    var (
        count = 10
        errs = make(chan error, count)
        wg =new(sync.WaitGroup)
    )
    wg.Add(count)
    go processErrors(errs)
    for i := 0; i < count; i++ {
        go func(i int, errs chan err) {
            wg.Done()
            err := fmt.Errorf("error %d.", i+1)
            errs<-err
        }(i, errs)
    }
    wg.Wait()
    close(errs)
    return
}

func processErrors(errs <-chan error) {
    for err := range errs{
        fmt.Println(err.Error())
    }
    return
}

这里要说明的一点是:channel的两端发送者和接受者goroutine都不能一直阻塞下去,若是一直阻塞那就是goroutine泄露。接口

相关文章
相关标签/搜索