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泄露。接口

相關文章
相關標籤/搜索