golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比

摘要:
看看执行效率,我们可以看到chan实现的性能并不理想,比sync的性能要好。Mutex实现的效率大约低25%。为什么chan的效率不理想?事实上,原因很简单。看看它的数据结构,您可以一目了然:packagemainimportfuncmain(){s:=“hello,world!”b: =[]字节:=makec˂-1fmt。Println}我们可以看到通道是由互斥体和数组实现的,其他一些字段是同步的,所以它的效率并不比直接使用互斥体快。许多看似高端和简单的东西实际上是由编辑器和运行时为代码农民实现的。当然,它们与额外的内存和时间成本有关。

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()
}

代码很简洁,我就不多解释了。

看一下执行效率    

golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比第1张

可以看到,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)
}

golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比第2张

我们可以看到channel的实现是通过互斥锁和数组的方式实现的,而且还有一些其他字段的同步,因此它的效率是不会比直接用互斥锁更快的。

很多看上去很高端很简洁的东西,其实是编辑器和runtime在后面为我们码农实现,当然与之相来是额外的内存和时间开销。

免责声明:文章转载自《golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇利用strace &amp;amp; Perf分析MySQLVSCode插件开发全攻略(四)命令、菜单、快捷键下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

golang解析json配置文件

安装 go get github.com/akkuman/parseConfig 使用说明 环境假设 . ├── config.go ├── config.json config.json内容 { "name" : "akkuman", "urls" : ["xx.com","ww.com"], "info" : {...

Deep Learning部署TVM Golang运行时Runtime

Deep Learning部署TVM Golang运行时Runtime 介绍 TVM是一个开放式深度学习编译器堆栈,用于编译从不同框架到CPU,GPU或专用加速器的各种深度学习模型。TVM支持来自Tensorflow,Onnx,Keras,Mxnet,Darknet,CoreML和Caffe2等各种前端的模型编译。TVM编译模块可以部署在LLVM(Java...

你知道Golang源码各目录的作用吗?

我是平也,这有一个专注Gopher技术与成长的开源项目「go home」 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目录总览 . ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── PATENTS ├── README.boringc...

godror基于ODPI-C的 oracle golang 驱动使用

godror 是一个很不错的oracle golang 驱动,基于ODPI-C 编写,好处是我们需要需要依赖编译 我们只需要安装oracle client 就可以了,不像go-oci8需要编译,同时目前也有一个纯golang 的 oracle驱动,但是目前还不是很稳定,以下是godror的参考使用 参考代码 package main ​ import (...

golang实现命令行程序的使用帮助

通过flag包我们可以很方便的实现命令行程序的参数标志,接下来我们来看看如何实现命令行程序的使用帮助,通常以参数标志-h或--help的形式来使用. 自动生成使用帮助 我们只需要声明其他参数标志,并执行解析,flag包会帮我们自动生成使用帮助. //main.go //输出2个数,输出较大的数 package main import (...

golang channel 用法总结

goroutine 使用golang的channel之前,我们需要先了解go的goroutine。Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。goroutine 是轻量级线程,相比线程开销更小,完全由 Go 语言负责调度,是 Go 支持并发的核心。如下所示,在go中我们可以很方便的开启并发执行。 package mai...