dictionary - 在 Go 中修改切片时如何避免竞争条件?
问题描述
func sample(testList []struct{}, testMap map[int64]struct{}) {
for i, test := range testList {
// some if conditions to get the matched key
testList[i] = testMap[key]
}
}
map 和 slice 的值是相同的类型。我将使用一些匹配的地图值来替换切片中的值。
解决方案
在 Go 中,保护对切片的并发访问有两个方面。
首先,切片是一个连续的内存块,包含零个或多个相同类型的元素;每个都通过其索引 ( 0
to len(slice)-1
) 访问。
在 Go 中,切片的每个元素都被视为一个独立变量,这意味着可以同时访问/修改同一切片的不同元素。重申一下,只要每次这样的访问,就可以从不同的并发运行的 goroutine
写入索引处的元素N
和同一切片的元素。M
N != M
因此,您只需要将多个 goroutine 执行的并发访问序列化到切片的同一元素。
换句话说,如果您需要N
从多个并发执行的 goroutine 中读取和/或修改索引处的元素,则需要通过互斥锁来保护该元素。
现在请注意,在现实生活中,保护单个互斥锁对单个元素的访问的需求很少发生,因此通常只使用单个互斥锁来保护对切片的任何元素的访问。
其次,切片上的某些操作可能会重新分配包含切片元素的内存块,这有点棘手:比如说,当你这样做时
slice = append(slice, a_new_element)
并且slice
没有空间容纳a_new_element
,会发生两件事:
append
将分配一个新的内存块来保存len(slice)+1
元素,然后将原始内存块的内存复制到那里。append
将返回一个新的切片描述符,并且slice
变量的内容将被=
操作员用它覆盖。
所有这些操作都会自然而然地与对该切片的任何涉及索引的访问(如)竞争slice[N]
:例如,在 goroutine 执行append
将复制切片的内存。
由此可见,对切片的任何可能重新分配切片内存块的操作以及对保存切片描述符的变量的任何更新都必须与正在修改切片元素的所有 goroutine 同步——无论这些 goroutine 是否同步修改单个切片的元素。
TL;博士
如果您要从多个 goroutine 访问切片,请使用单个互斥锁来保护对切片的任何类型的访问。
如果您最终会检测到互斥体将成为瓶颈,那么您可以使用上述逻辑来降低争用。
推荐阅读
- c# - 字符串中多次出现的索引
- micronaut - 如何解决 micronaut 应用程序中的错误“没有 [io.micronaut.data.operations.PrimaryRepositoryOperations] 类型的 bean”?
- arrays - 如何遍历两个数组并在 Ruby 中创建地图
- java - 以对象或字符串作为参数覆盖
- python - Dockerized 烧瓶应用程序在本地构建和运行,但在 Azure 上部署时无法运行
- mysql - 如何将容器 IP 地址传递给另一个容器?
- node.js - 应用程序不会在 MongoDB 本地数据库上存储任何内容
- ios - 如何向 UITableViewCells Swift 添加多个按钮
- javascript - 如何在 AngularJS 中向 app.module() 动态添加控制器?
- swift - 快速惰性序列 - 为什么在引入定义时编译器会抱怨