golang開發:CSP-WaitGroup Mutex

飛翔碼農發表於2020-09-15

CSP 是 Communicating Sequential Process 的簡稱,中文可以叫做通訊順序程式,是一種併發程式設計模型,最初於Tony Hoare的1977年的論文中被描述,影響了許多程式語言的設計。

golang CSP模型

golang語言並沒有完全實現了CSP模型的所有理論,僅僅是借用了 process和channel這兩個概念。process是在golang語言上的表現就是 goroutine, 是實際併發執行的實體,每個實體之間是通過channel通訊來實現資料共享。
最經典的資料通訊共享理論

以通訊的方式來共享記憶體

Do not communicate by sharing memory; instead, share memory by communicating

不要以共享記憶體的方式來通訊,相反,要通過通訊來共享記憶體。
這是golang實現高併發的基礎通訊理論。
在golang語言上面,就是通過channel實現多個goroutine的資料通訊。

sync.WaitGroup使用

如果需要讓多個goroutine都執行完成,就是用使用time.sleep,延遲幾秒保證每個goroutine都能執行到。

func testPrint(i int) {
	fmt.Println(i)
}
func main() {
	for i:=0;i<5;i++ {
		go testPrint(i)
	}
	time.Sleep(time.Second)
}

但是在生產專案裡面就沒法這麼寫了,延遲1S有可能所有goroutine沒執行完成,延遲1000S可能1毫秒就執行完了,其他999S都是等待,延長了程式執行時間。
所以就有sync.WaitGroup

func testPrint(wg *sync.WaitGroup, i int) {
	fmt.Println(i)
	wg.Done()
}
func main() {
	var wg = new(sync.WaitGroup)
	for i:=0;i<5;i++ {
		wg.Add(1)
		go testPrint(wg,i)
	}
	wg.Wait()
}

WaitGroup比較容易理解,其實就是一個內部計數器,在執行goroutine行為之前執行 wg.Add(1),給計數器+1,執行完之後,執行wg.Done(),表示這個goroutine執行完成,計數器內部-1,wg.Wait()會阻塞程式碼的執行,等待所有的新增進WaitGroup的goroutine全部執行完畢(計數器減為0),再退出程式。
非常完美的解決了等待所有goroutine執行完畢的需要。

sync.Mutex

sync.Mutex,互斥鎖排它鎖。
我們需要維護一個變數,保證每個goroutine都能成功的修改它,如果沒有互斥鎖可能就是下面的程式碼

func testPrint(wg *sync.WaitGroup, i int) {
	count++
	fmt.Println(i)
	wg.Done()
}
var count int
func main() {
	count = 0
	var wg = new(sync.WaitGroup)
	for i:=0;i<500;i++ {
		wg.Add(1)
		go testPrint(wg,i)
	}
	wg.Wait()
	fmt.Printf("count:%d",count)
}

預期應該是特定的值,500,其實經常性的不是500.就是因為多個goroutine同時去修改count值了,加上互斥鎖試一下。

func testPrint(wg *sync.WaitGroup, i int) {
	defer func() {
		mu.Unlock()
	}()
	mu.Lock()
	count++
	fmt.Println(i)
	wg.Done()
}
var count int
var mu *sync.Mutex
func main() {
	count = 0
	mu = new(sync.Mutex)
	var wg = new(sync.WaitGroup)
	for i:=0;i<500;i++ {
		wg.Add(1)
		go testPrint(wg,i)
	}
	wg.Wait()
	fmt.Printf("count:%d",count)
}

結果是定值500,跟預想的一致。

相關文章