内容简介:1、首先,在开始之前,先说一点相关的东西。在 Golang 中,有很多数据结构的操作,都不是线程安全的,比如大家熟知的 map ,比如 container/list 包。线程安全,指的是基于这类数据结构实例化的变量,可以并发操作,也就是多个 goroutine 同时进行操作。另外,也许你也知道,golang 在编译时,是支持并发竞争检测的。go build --race ,很多 gopher 其实并不陌生。这里需要说一点是,--race 并非只支持构建时,也支持单测时,也就是 go test --race。
问题示例
1、首先,在开始之前,先说一点相关的东西。
在 Golang 中,有很多数据结构的操作,都不是线程安全的,比如大家熟知的 map ,比如 container/list 包。线程安全,指的是基于这类数据结构实例化的变量,可以并发操作,也就是多个 goroutine 同时进行操作。
另外,也许你也知道,golang 在编译时,是支持并发竞争检测的。go build --race ,很多 gopher 其实并不陌生。这里需要说一点是,--race 并非只支持构建时,也支持单测时,也就是 go test --race。
好了,结合上面2个点,我们看一个例子(文件 xx_test.go)(代码示例1):
代码很简单,初始化一个切片,起2个协程,并发操作这个切片。
我们做一下单测并做竞争检测:
从结果来看,竞争检测结果是通过的。
2、我们将上面的代码做一点变更,上面代码第 9 行,切片初始化是这样的:
我们做一个改动,给它一个大小
仅此而已,什么都不变,然后我们再看一下完整的代码,并再次做一次竞争检测(代码示例2)。
我们再次做测试,看一下测试结果:
结果:
很直接,golang 直接告诉我们有数据竞争,数据竞争检测不通过。而我们仅仅只改了 slice 的初始化方式而已。
为什么测试会失败
要理解为什么会失败,就需要看我们2个例子中,切片的内存变化。
代码示例1的切片内存布局
竞争检测通过的代码示例1(也就是第一个代码例子)中的 x 初始化方式我们回顾一下:
在这个切片中,名称为 x ,长度为 1,容量也为 1 。
但是需要注意,在代码示例1,2个不同的协程,要向 x 中分别添加元素:"hello", "world" 和 "goodbye", "bob" ,所以,Golang 需要新开辟内存空间,切片 x 的内存变化如图:
这个图有几个关键点:
-
原始切片为 x,长度和容量都是 1。
-
协程1为切片 x,添加元素,并将结果赋值给新的变量 y。相当于直接开辟了内存空间 y,做元素新增的操作。
-
协程2为切片 x,添加元素,并将结果赋值给新的变量 z。相当于直接开辟了内存空间 z,做元素新增的操作。
-
当多个线程读取内存 x 时,由于 x 底层一直就没变化,因此,不会发生数据争用。竞争检测是通过的。
代码示例2的切片内存布局
在后来的例子,也就是代码示例2中,代码有所变化,我们回顾一下:
从图中可以看到,切片 x 的内存布局有所变化,长度为 0,但是容量为 6。在代码示例2中,有2个协程,在往 x 中,分别添加2个 元素。问题是,在这个切片 x 中,是有足够的空间,可以放下 6个新元素的。因此,协程1和协程2,都会往切片 x 的内存空间中,添加新元素。
而竞争,就是发生是因为两个goroutine都试图写入相同的内存区域。因此,数据竞争产生了。golang test --race 也就失败了。
竞争对切片 x 写数据的示意图如下:
如图,协程1 和 协程2 ,竞争操作了同一个切片 x。最终也不知道谁赢了。
结论:
在 Golang 的切片操作中,每次调用 append 并不会强制执行新的内存分配。因此,上面的情况,这是 golang 本身的特性,而不是bug。
如何避免上述问题
解决方式1:预先分配好目标变量内存
最简单的解决方法是,做 append 操作时,如果你希望 append 后是一个新的数据,那么,一开始就不要不使用有共享状态的变量,作为要追加的第一个变量。
比如,使用你需要的总容量创建一个新切片,并使用新切片作为要追加的第一个变量。
下面是一个代码示例:
总的来说(以协程1的操作为例):
-
append 之前,先创建新的变量 y。
-
将 x 原有的数据,添加到 y 中。
-
执行你需要 append 的新元素。
这个操作其实有点繁琐,谈不上优雅,而且内存效率也有一定程度上的浪费。
解决方式2:加锁
当然,如果你有更好的解决方式,欢迎指正。
欢迎关注“海角之南”公众号获取更新动态
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Web整合开发王者归来
刘京华 / 清华大学 / 2010-1 / 99.80元
《Java Web整合开发王者归来(JSP+Servlet+Struts+Hibernate+Spring)》全面介绍了Java Web开发中的各种相关技术及知识。全书分为9篇,内容层次清晰,难度循序渐进。第1篇为入门篇,内容包括Java Web开发概述等;第2篇为基础篇,内容包括Servlet技术、JSP技术、会话跟踪、过滤器Filter、监听器Listener等;第3篇为高级篇,内容包括JST......一起来看看 《Java Web整合开发王者归来》 这本书的介绍吧!