# Go Lang 併發 等待/修改變數 作法 ###### tags: `GoLang` ## 介紹 在Go Lang要執行併發十分簡單,但要控制及確保參數使用還需要做一些了解,這邊針對兩種一般常使用的操作來實。 ## 目錄 [TOC] # 一、前言 在[Go Lang 學習筆記 - 環境建立與基本語法](/qv8vk7PmTGKd0XlmF-pCWw)結論的地方有說明GoLang的特色,該文章也有稍微提到併發的使用和等待,這邊針對使用方式在多些實作範例。 相要直接看程式碼的這邊直接附上[GitHub](https://github.com/franksu129/play-go/tree/main/GoConcurrency),以下實作也是針對該專案來說明。 # 二、實作步驟 這邊分成兩類常遇到的操來實作,以下範例皆需要執行才可以看到效果,因此不會附上結果畫面 ## Goroutine 修改共同參數 ### 1. channel 這邊可以看到`for`迴圈底下執行了1000個非同步的程序,在程序中我們使用`channel`來進行參數取得、操作並回寫 [RunCode](https://go.dev/play/p/W9mkMG4Nw2Z) ```go= func main(){ total := 0 // 宣告容量為1, 這樣channel再放入1個物件不會進入等待狀態 ch := make(chan int, 1) ch <- total for i := 0; i < 1000; i++ { // 進行非同步作業 go func() { ch <- <-ch + 1 }() } // 等待一秒的時間 time.Sleep(time.Second) fmt.Println(<-ch) } ``` ### 2. sync.Mutex 所謂的互斥鎖,我自己則把它看成C#的Lock對我比較好理解,其作法如下: [RunCode](https://go.dev/play/p/AgmqLru4mZ5) ```go= type SafeNumber struct { v int mux sync.Mutex } func main() { total := SafeNumber{v: 0} for i := 0; i < 1000; i++ { go func() { // 進行 Lock total.mux.Lock() // 取得、修改並回寫 total.v++ // 修改完成後解開 total.mux.Unlock() }() } time.Sleep(time.Second) // 進行 Lock total.mux.Lock() // 取得 fmt.Println(total.v) // 修改完成後解開 total.mux.Unlock() } ``` ## 等待Goroutine ### 1. channel 這邊藉由`Channel`的特性來進行等待 [RunCode](https://go.dev/play/p/ar6ncgt03ve) ```go= func main(){ ch := make(chan string) go sayTakeCh("Hello ch", ch) // 此處會等待接收ch資料 <-ch } func sayTakeCh(s string, c chan string) { for i := 0; i < 5; i++ { time.Sleep(1000 * time.Millisecond) fmt.Println(s) } // 透過 channel 告知該緒結束 c <- "FINISH" } ``` ### 2. sync.WaitGroup 第二種方式則是透過`sync`套件來執行,使用`sync.WaitGroup`提供的方法,這也是在[Go Lang 學習筆記 - 環境建立與基本語法](/qv8vk7PmTGKd0XlmF-pCWw)裡面實作的做法 [RunCode](https://go.dev/play/p/wLT3JTUPrpz) ```go= func main() { wg := new(sync.WaitGroup) wg.Add(1) go sayTakeWG("Hello wg", wg) // 直接等待任務 wg.Wait() } func sayTakeWG(s string, wg *sync.WaitGroup) { // defer 定義 func 結束後執行 defer wg.Done() for i := 0; i < 5; i++ { time.Sleep(1000 * time.Millisecond) fmt.Println(s) } } ``` ### 3. time.Sleep 此外也可以直接等待固定時間來進行任務的等待,不過因為在實際應用上較少能確保邏輯上的運行時間,故此作法也不推薦,在**Goroutine 修改共同參數**底下的實作就是用該方式,這邊就不在實作了。 # 結論 這邊單純針對併發上會進行的兩種操作快速說明,不過可以發現Channel能做的事情很多,未來可以針對Channel的使用做更深入的了解,這樣未來在使用上會更加順手。 <br/> --- <style> .red{color: red;} </style>