iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
0
Software Development

Go Distributed & Go Consistently系列 第 11

Day11 Mutex vs Channel

昨天看過 race condiction 的情況後,我們了解了 mutex.Lock 與 channel,可以幫助我們同步 memory 狀態,避免發生奇怪的資料異常狀態。今天我們要延續昨天的技巧,並且用 benchmark 比較兩個在速度上的優劣。

Benchmark

我們透過測試範例,可以看出 mutex 每次操作僅需 215 ns/op,而 channel 則需 538 ns/op。也就是在一般情況下,如果我們只是想避免變數上的污染,大可放心的使用較簡易的 mutex lock。畢竟他效能佳且使用較為直覺。

> go test -v -bench=. -run=none -benchmem ./basicGo/benchmark/mutex_test.go

goos: darwin
goarch: amd64
BenchmarkMutex
BenchmarkMutex-4         5752076               215 ns/op               0 B/op          0 allocs/op
BenchmarkChannel
BenchmarkChannel-4       2011600               538 ns/op              76 B/op          0 allocs/op
PASS
ok      command-line-arguments  3.360s

Pass Between Goroutines

只看 benchmark 的話,可能會覺得 channel 沒有發揮的空間,但其實不然。在一些較複雜的多 goroutines 情境下,goroutine 各自過 channel 來做資料的傳遞,可以在不同的 goroutine 間傳遞異動資料。

  • 生產線範例
    • 三個產線各自處理牛肉、起司、酸黃瓜
    • 每條產線中間僅有大小為 10個漢堡的緩衝空間
    • 產線末端需統計是否達成目標

channel/main.go

package main

import "fmt"

//Burger 就是個漢堡
type Burger struct {
	beef    bool
	cheese  bool
	pickles bool
}

var (
	addBeff    = make(chan Burger, 10) //超過10筆後阻塞
	addCheese  = make(chan Burger, 10) //超過10筆後阻塞
	addPickles = make(chan Burger, 10) //超過10筆後阻塞
	done       = make(chan []Burger)
)

func main() {
	go AddBeff()    //加牛肉監聽開始
	go AddCheese()  //加起司監聽開始
	go AddPickles() //加酸黃瓜監聽開始

	for {
		select {
		//達成後停止
		case count := <-done:
			for i := range count {
				if !count[i].beef || !count[i].cheese || !count[i].pickles {
					fmt.Println("oops!", count)
					return
				}
			}
			fmt.Println(len(count), "success!")
			return
		//開始製造漢堡
		case addBeff <- Burger{}:
		}
	}
}

//AddBeff 加牛肉
func AddBeff() {
	for {
		//一次只能加一塊牛肉
		select {
		case burger := <-addBeff:
			burger.beef = true
			addCheese <- burger
		}
	}
}

//AddCheese 加起司
func AddCheese() {
	for {
		//一次只能加一塊起司
		select {
		case burger := <-addCheese:
			burger.cheese = true
			addPickles <- burger
		}
	}
}

//AddPickles 加酸黃瓜
func AddPickles() {
	var count []Burger
	for {
		//一次只能加一份酸黃瓜
		select {
		case burger := <-addPickles:
			burger.pickles = true
			//漢堡完成
			count = append(count, burger)

			//達成目標
			if len(count) == 1000 {
				done <- count
			}
		}
	}
}

上一篇
Day10 Race Condition
下一篇
Day12 Atomic
系列文
Go Distributed & Go Consistently30

尚未有邦友留言

立即登入留言