golang 併發程式設計之生產者消費者

hatlonely發表於2018-03-11

golang 最吸引人的地方可能就是併發了,無論程式碼的編寫上,還是效能上面,golang 都有絕對的優勢

學習一個語言的併發特性,我喜歡實現一個生產者消費者模型,這個模型非常經典,適用於很多的併發場景,下面我通過這個模型,來簡單介紹一下 golang 的併發程式設計

go 併發語法

協程 go

協程是 golang 併發的最小單元,類似於其他語言的執行緒,只不過執行緒的實現藉助了作業系統的實現,每次執行緒的排程都是一次系統呼叫,需要從使用者態切換到核心態,這是一項非常耗時的操作,因此一般的程式裡面執行緒太多會導致大量的效能耗費線上程切換上。而在 golang 內部實現了這種排程,協程在這種排程下面的切換非常的輕量級,成百上千的協程跑在一個 golang 程式裡面是很正常的事情

golang 為併發而生,啟動一個協程的語法非常簡單,使用 go 關鍵字即可

go func () {
    // do something
}()

同步訊號 sync.WaitGroup

多個協程之間可以通過 sync.WaitGroup 同步,這個類似於 Linux 裡面的訊號量

var wg sync.WaitGroup  // 申明一個訊號量
wg.Add(1)   // 訊號量加一
wg.Done()   // 訊號量減一
wg.Wait()   // 訊號量為正時阻塞,直到訊號量為0時被喚醒

通道 chan

通道可以理解為一個訊息佇列,生產者往佇列裡面放,消費者從佇列裡面取。通道可以使用 close 關閉

ic := make(chan int, 10)  // 申明一個通道
ic <- 10        // 往通道里面放
i := <- ic      // 從通道里面取

close(ic)       // 關閉通道

生產者消費者實現

定義產品類

這個產品類根據具體的業務需求定義

type Product struct {
    name  int
    value int
}

生產者

如果 stop 標誌不為 false,不斷地往通道里面放 product,完成之後訊號量完成

func producer(wg *sync.WaitGroup, products chan<- Product, name int, stop *bool) {
    for !*stop {
        product := Product{name: name, value: rand.Int()}
        products <- product
        fmt.Printf("producer %v produce a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}

消費者

不斷地從通道里面取 product,然後作對應的處理,直到通道被關閉,並且 products 裡面為空, for 迴圈才會終止,而這正是我們期望的

func consumer(wg *sync.WaitGroup, products <-chan Product, name int) {
    for product := range products {
        fmt.Printf("consumer %v consume a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}

主執行緒

var wgp sync.WaitGroup
var wgc sync.WaitGroup
stop := false
products := make(chan Product, 10)

// 建立 5 個生產者和 5 個消費者
for i := 0; i < 5; i++ {
    go producer(&wgp, products, i, &stop)
    go consumer(&wgc, products, i)
    wgp.Add(1)
    wgc.Add(1)
}

time.Sleep(time.Duration(1) * time.Second)
stop = true     // 設定生產者終止訊號
wgp.Wait()      // 等待生產者退出
close(products) // 關閉通道
wgc.Wait()      // 等待消費者退出

> 轉載請註明出處 > 本文連結:<http://hatlonely.github.io/2018/03/11/golang-%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85/>

更多原創文章乾貨分享,請關注公眾號
  • golang 併發程式設計之生產者消費者
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章