根据Go博客,
地图对于并发使用是不安全的:它没有定义当您同时读写地图时会发生什么。如果需要从并发执行的goroutines读取映射和向映射写入映射,则访问必须通过某种同步机制进行调解。(来源:https://blog.golang.org/go-maps-in-action)
有人能详细说明一下吗?跨例程的并发读取操作似乎是允许的,但是如果尝试读取和写入同一个键,并发读取/写入操作可能会生成竞争条件。
在某些情况下,这最后一个风险是否可以降低?例如:
这不是代码(很明显),但我认为它显示了一种情况的概要,即即使a和B都尝试访问m,也不会有竞争条件,或者如果有竞争条件,也不会因为附加的约束而有什么关系。
Go 1.6发行说明
运行时增加了对并发滥用地图的轻量级、尽力而为的检测。与往常一样,如果一个goroutine正在写入映射,那么其他goroutine不应该同时读取或写入映射。如果运行时检测到这种情况,它将打印诊断并使程序崩溃。了解更多问题的最佳方法是在种族检测器下运行程序,这将更可靠地识别种族并提供更多细节。
地图是复杂的、自组织的数据结构。未定义并发读写访问。
没有代码,就没有什么好说的了。
并发读取(只读)正常。并发写入和/或读取不正常。
如果访问是同步的,多个Goroutine只能写入和/或读取同一映射,例如通过同步包、通道或其他方式。
您的示例:
您的示例有两个goroutine:A和B,A尝试同时读取m(在步骤3中),B尝试同时写入m(在步骤4中)。没有同步(您没有提到任何同步),因此这是不允许/不确定的。
这是什么意思?未确定意味着即使B写入m,A也可能无法观察到变化。或者A可能观察到一个甚至没有发生的变化。否则可能会出现恐慌。或者,由于这种非同步并发访问,地球可能会爆炸(尽管后一种情况发生的可能性非常小,甚至可能小于1e-40)。
相关问题:
具有并发访问的映射
Go中的地图没有线程安全意味着什么?
在Go中使用地图时,忽略goroutine/线程安全有什么危险?
在Golang 1.6之前,并发读取正常,并发写入不正常,但写入和并发读取正常。自Golang 1.6以来,地图在书写时无法读取。因此,在Golang 1.6之后,并发访问映射应该如下所示:
package main
import (
"sync"
"time"
)
var m = map[string]int{"a": 1}
var lock = sync.RWMutex{}
func main() {
go Read()
time.Sleep(1 * time.Second)
go Write()
time.Sleep(1 * time.Minute)
}
func Read() {
for {
read()
}
}
func Write() {
for {
write()
}
}
func read() {
lock.RLock()
defer lock.RUnlock()
_ = m["a"]
}
func write() {
lock.Lock()
defer lock.Unlock()
m["b"] = 2
}
添加:
您可以使用go run-race race检测比赛。转到
更改读取功能:
func read() {
// lock.RLock()
// defer lock.RUnlock()
_ = m["a"]
}
另一种选择:
正如我们所知,map是由bucket和sync实现的。RWMutex将锁定所有存储桶。并发映射使用fnv32共享密钥,每个bucket使用一个同步。RWMutex。
问题内容: 要求仅必须允许单个线程执行用户管理(创建/更新/导入)操作,但不允许多个线程同时为同一用户执行用户操作。例如,当线程A正在创建用户A时,必须不允许线程B同时允许线程B导入用户A或创建用户A,但是允许线程B导入用户B。下面的代码线程对于这些要求是否安全? 问题答案: 除了安德鲁·莱金(Andrew Lygin)提到的程序外,您的程序还有另一个错误。 设置为if以前未见过,因为`putIf
tcp套接字是具有双向读写功能的endpoint。在java中,我们可以获得套接字的InputStream和OutputStream。 同时使用这些流是否安全? 据我所知,有一个连接能够在任何给定时间从一个endpoint发送或接收到其他数据。 我正在基于SocketChannels实现nio传输层,我想保留一个线程用于所有写入,一个线程用于接受和读取,但我不确定如果我的线程同时尝试在同一个套接字
问题内容: 我的应用程序中有多个线程同时访问BitSet。该文档说: 如果没有外部同步,则BitSet对于多线程使用是不安全的。 它没有说读或写是否不安全。谁能解释。 问题答案: 仅当初始化的最后一个操作与读取该操作的操作之间存在“先于”关系时,A 对于只读操作才是安全的。 最简单的方法是使用。例如: 这足以确保“安全发布”。 但是,如果您不执行此类操作,则无法保证读取的线程将看到完全初始化的状态
Go Env: goarch=“AMD64” goroot=“/usr/local/go” goTooldir=“/usr/local/go/pkg/tool/linux_amd64” go15vendorexperiment=“1” go版本GO1.6.3 Linux/AMD64 这个问题发生在高负载的“性能测试环境”库贝-APIServer上。库贝-Apiserver恐慌和退出: 相应源代码:
我正在阅读JCIP,无法理解3.3.1中的以下语句, 对共享的易失性变量执行读-修改-写操作是安全的,只要可以确保仅从单个线程写入易失性变量。在本例中,您将修改限制在单个线程中,以防止出现争用情况,并且volatile变量的可见性保证确保其他线程看到最新的值。 即使挥发性变量只从单个线程写入,它怎么能不提高竞争条件?如果线程A在计数器为1时执行,线程B可以在计数器1写入内存之前进入,并获得的旧值1
问题内容: 如何从“并发映射读取和映射写入”的运行时恐慌中恢复?通常,恢复时的延迟似乎不起作用。这是为什么? 我知道您不应该在并发上下文中使用地图,但是仍然:如何在此处恢复? 例: 请添加恢复代码。:) 问题答案: 恢复在这里不起作用,因为您遇到的不是紧急状态。 Go 1.6向 运行时添加了轻量级的并发滥用地图检测功能: 运行时增加了轻巧,尽力而为的检测并发滥用地图的功能。与往常一样,如果一个go