golang 並發程序寫入map兩種實現方式sync.Mutex和chan的效率對比


golang原生的數據結構map,由於是通過hash方式實現的,不支持並發寫入,但是在golang很多並發場景中,不可避免的需要寫入map,下面介紹兩種解決map並發寫入的實現方式:

  1. sync.Mutex互斥鎖(通過加鎖解鎖解決map不能並發寫入的問題)
  2. chan (通過管道來解決map並發的問題),chan的存在完美解決goroutine之間的通信以及數據race問題,但是它的性能如何呢,下面讓我們來測一下

首先先上代碼:

package main

import (
"testing"
"sync"
)

func BenchmarkTest(b *testing.B) {
var m = make(map[int]int, 10000)
var mu = &sync.Mutex{}
var c = make(chan int, 200)
var w = &sync.WaitGroup{}

w.Add(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c <- 1
go func(index int) {
mu.Lock()
m[index] = 0
mu.Unlock()
w.Done()
<- c
}(i)
}
w.Wait()
}

func BenchmarkTestBlock(b *testing.B) {
var m = make(map[int]int, 10000)
var c = make(chan int, 200)
var dataChan = make(chan int, 200)
var w = &sync.WaitGroup{}

w.Add(b.N+1)
go func() {
for i := 0; i < b.N; i++ {
m[<- dataChan] = 0
}
w.Done()
}()

b.ResetTimer()
for i := 0; i < b.N; i++ {
c<-1
go func(index int) {
dataChan <- index
w.Done()
<-c
}(i)
}
w.Wait()
}

代碼很簡潔,我就不多解釋了。

看一下執行效率    

 

可以看到,chan的實現方式其性能並不理想,比sync.Mutex的實現方式的效率低了大約25%左右。 

chan的效率為什么不理想呢,其實原因很簡單,下面看一下它的數據結構就一目了然了

package main

import (
    "fmt"
)

func main() {
    s := "hello, world!"
    b := []byte(s)
    c := make(chan int, 1)
    c<-1
    fmt.Println(s, b, <-c)
}

 

我們可以看到channel的實現是通過互斥鎖和數組的方式實現的,而且還有一些其他字段的同步,因此它的效率是不會比直接用互斥鎖更快的。

很多看上去很高端很簡潔的東西,其實是編輯器和runtime在后面為我們碼農實現,當然與之相來是額外的內存和時間開銷。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM