Go 併發concurrency 學習筆記

衣舞晨風發表於2017-08-13
  • 很多人都是衝著 Go 大肆宣揚的高併發而忍不住躍躍欲試,但其實從原始碼的解析來看,goroutine 只是由官方實現的超級“執行緒池”而已。不過話說回來,每個例項 4-5KB 的棧記憶體佔用和由於實現機制而大幅減少的建立和銷燬開銷,是製造 Go 號稱的高併發的根本原因。另外,goroutine 的簡單易用,也在語言層面上給予了開發者巨大的便利。
  • 併發不是並行:Concurrency Is Not Parallelism
  • 併發主要由切換時間片來實現“同時”執行,在並行則是直接利用多核實現多執行緒的執行,但 Go 可以設定使用核數,以發揮多核計算機的能力。
  • Goroutine 奉行通過通訊來共享記憶體,而不是共享記憶體來通訊。

Channel

  • Channel 是 goroutine 溝通的橋樑,大都是阻塞同步的
  • 通過 make 建立,close 關閉
  • Channel 是引用型別
  • 可以使用 for range 來迭代不斷操作 channel
  • 可以設定單向或雙向通道
  • 可以設定快取大小,在未被填滿前不會發生阻塞

通過channel計算10次,累加

方法一:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Println("NumCPU:", runtime.NumCPU())
    //獲取cpu核心數量
    runtime.GOMAXPROCS(runtime.NumCPU())
    // channel bool型別 快取長度為10
    c := make(chan bool, 10)
    for i := 0; i < 10; i++ {
        go Go(c, i)
    }
    for i := 0; i < 10; i++ {
        <-c
    }
}

func Go(c chan bool, index int) {
    a := 1
    for i := 0; i < 100000000; i++ {
        a += i
    }
    fmt.Println(index, a)
    c <- true
}

輸出結果:
這裡寫圖片描述

方法二:

package main

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

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    wg := sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go Go(&wg, i)
    }
    wg.Wait()
}

func Go(wg *sync.WaitGroup, index int) {
    a := 1
    for i := 0; i < 100000000; i++ {
        a += i
    }
    fmt.Println(index, a)
    wg.Done()
}

輸出結果:
這裡寫圖片描述

Select

  • 可處理一個或多個 channel 的傳送與接收
  • 同時有多個可用的 channel時按隨機順序處理
  • 可用空的 select 來阻塞 main 函式
  • 可設定超時

1、處理多個 channel

package main

import (
    "fmt"
)

func main() {
    c1, c2 := make(chan int), make(chan string)
    o := make(chan bool, 2)
    go func() {
        for {
            select {
            case v, ok := <-c1:
                if !ok {
                    o <- true
                    break
                }
                fmt.Println("c1", v)
            case v, ok := <-c2:
                if !ok {
                    o <- true
                    break
                }
                fmt.Println("c2", v)
            }
        }

    }()

    c1 <- 1
    c2 <- "hhhh"

    c1 <- 9
    c2 <- "uuuuu"

    close(c1)
    close(c2)

    for i := 0; i < 2; i++ {
        <-o
    }
}

輸出結果:
這裡寫圖片描述

2、同時有多個可用的 channel時按隨機順序處理

package main

import (
    "fmt"
)

func main() {
    o := make(chan int)
    go func() {
        for v := range o {
            fmt.Println(v)
        }

    }()

    for {
        select {
        case o <- 1:
        case o <- 2:
        }
    }
}

輸出結果:
這裡寫圖片描述

3、超時

package main

import (
    "fmt"
    "time"
)

func main() {
    o := make(chan int)

    select {
    case v := <-o:
        fmt.Println(v)
    case <-time.After(3 * time.Second):
        fmt.Println("TimeOut")
    }

}

輸出結果:
這裡寫圖片描述

本文參考整理自:《Go 程式設計基礎》

個人微信公眾號:
這裡寫圖片描述

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

相關文章