当前位置: 首页 > 知识库问答 >
问题:

Golang映射用于并发读/写操作的安全性如何?

艾照
2023-03-14

根据Go博客,

地图对于并发使用是不安全的:它没有定义当您同时读写地图时会发生什么。如果需要从并发执行的goroutines读取映射和向映射写入映射,则访问必须通过某种同步机制进行调解。(来源:https://blog.golang.org/go-maps-in-action)

有人能详细说明一下吗?跨例程的并发读取操作似乎是允许的,但是如果尝试读取和写入同一个键,并发读取/写入操作可能会生成竞争条件。

在某些情况下,这最后一个风险是否可以降低?例如:

  • 函数A生成k并将m[k]=0。这是唯一一次A写入映射m。已知k不在m中。

这不是代码(很明显),但我认为它显示了一种情况的概要,即即使a和B都尝试访问m,也不会有竞争条件,或者如果有竞争条件,也不会因为附加的约束而有什么关系。

共有3个答案

丁文轩
2023-03-14

Go 1.6发行说明

运行时增加了对并发滥用地图的轻量级、尽力而为的检测。与往常一样,如果一个goroutine正在写入映射,那么其他goroutine不应该同时读取或写入映射。如果运行时检测到这种情况,它将打印诊断并使程序崩溃。了解更多问题的最佳方法是在种族检测器下运行程序,这将更可靠地识别种族并提供更多细节。

地图是复杂的、自组织的数据结构。未定义并发读写访问。

没有代码,就没有什么好说的了。

闾丘康安
2023-03-14

并发读取(只读)正常。并发写入和/或读取不正常。

如果访问是同步的,多个Goroutine只能写入和/或读取同一映射,例如通过同步包、通道或其他方式。

您的示例:

  1. 函数A生成k并将m[k]=0。这是唯一一次A写入映射m。已知k不在m中。

您的示例有两个goroutine:A和B,A尝试同时读取m(在步骤3中),B尝试同时写入m(在步骤4中)。没有同步(您没有提到任何同步),因此这是不允许/不确定的。

这是什么意思?未确定意味着即使B写入m,A也可能无法观察到变化。或者A可能观察到一个甚至没有发生的变化。否则可能会出现恐慌。或者,由于这种非同步并发访问,地球可能会爆炸(尽管后一种情况发生的可能性非常小,甚至可能小于1e-40)。

相关问题:

具有并发访问的映射

Go中的地图没有线程安全意味着什么?

在Go中使用地图时,忽略goroutine/线程安全有什么危险?

许承悦
2023-03-14

在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