为什么此代码会导致数据争用?

2023-12-04

1 package main
2
3 import "time"
4
5 func main() {
6     m1 := make(map[string]int)
7     m1["hello"] = 1
8     m1["world"] = 2
9     go func() {
10         for i := 0; i < 100000000; i++ {
11             _ = m1["hello"]
12         }
13     }()
14     time.Sleep(100 * time.Millisecond)
15     m2 := make(map[string]int)
16     m2["hello"] = 3
17     m1 = m2
18 }

我运行命令go run --race使用此代码并得到:

==================
WARNING: DATA RACE
Read at 0x00c420080000 by goroutine 5:
  runtime.mapaccess1_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
  main.main.func1()
      /Users/meitu/test/go/map.go:11 +0x80

Previous write at 0x00c420080000 by main goroutine:
  runtime.mapassign()
      /usr/local/go/src/runtime/hashmap.go:485 +0x0
  main.main()
      /Users/meitu/test/go/map.go:16 +0x220

Goroutine 5 (running) created at:
  main.main()
      /Users/meitu/test/go/map.go:13 +0x1aa
==================

m1 and m2是不同的变量,为什么第16行和第11行会导致数据竞争?

我的go版本是1.8。我想这是一些编译优化,有人可以告诉我吗?非常感谢。


The 要求进行数据竞赛是:

  1. 多个 goroutine访问相同的资源(例如变量)同时.
  2. 这些访问中至少有一个是write.
  3. 访问权限是不同步.

在您的代码中,满足了所有 3 个要求:

  1. 你有主 goroutine 访问m1,并且您在其中启动的那个也可以访问m1。主 Goroutine 在其他 Goroutine 启动后访问它,所以它们是同时.
  2. 主goroutine写的是m1第 17 行:m1 = m2.
  3. 访问不同步,您不使用互斥体或通道或类似的东西(睡眠是not同步)。

因此,这是一场数据竞赛。

明显的数据竞争发生在 #11 行之间m1,以及第 17 行写作m1.

但!由于第 17 行分配了m2 to m1,那么当/如果启动的 goroutine 继续运行,它会尝试读取m1 which 现在可能是的价值m2因为我们分配了m2 to m1。这是什么意思?这就引入了另一种数据竞赛的写法m2和阅读m1.

即在第 #17 行之后,如果程序没有立即结束(可能但不一定),则启动的 goroutine 会尝试从m1现在是m2最后写在第 #16 行,因此这解释了第 #11 行和第 #16 行之间的“冲突”。

完整的go run -race输出如下:

==================
WARNING: DATA RACE
Write at 0x00c42000e010 by main goroutine:
  main.main()
      /home/icza/gows/src/play/play2.go:17 +0x22f

Previous read at 0x00c42000e010 by goroutine 5:
  [failed to restore the stack]

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c42007e000 by goroutine 5:
  runtime.mapaccess1_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
  main.main.func1()
      /home/icza/gows/src/play/play2.go:11 +0x7a

Previous write at 0x00c42007e000 by main goroutine:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:598 +0x0
  main.main()
      /home/icza/gows/src/play/play2.go:16 +0x1fc

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c420080088 by goroutine 5:
  main.main.func1()
      /home/icza/gows/src/play/play2.go:11 +0x90

Previous write at 0x00c420080088 by main goroutine:
  main.main()
      /home/icza/gows/src/play/play2.go:16 +0x212

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
Found 3 data race(s)
exit status 66
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么此代码会导致数据争用? 的相关文章

  • 为什么结构中“[0]byte”的位置很重要?

    0 byte在golang中不应该占用任何内存空间 但这两个结构体的大小不同 type bar2 struct A int 0 byte type bar3 struct 0 byte A int 那么为什么这个位置 0 byte这里重要吗
  • GAE Go — 如何对不存在的实体键使用 GetMulti?

    我发现自己需要做一个GetMulti使用键数组进行操作 其中某些实体存在 但有些实体不存在 我当前的代码 如下 返回错误 datastore no such entity err datastore GetMulti c keys info
  • for 循环初始值设定项中的结构

    知道为什么 for 循环初始值设定项中的这个结构表达式在编译时会出现语法错误吗 在这种情况下 指向结构的指针工作正常 但 ofc 我需要如下所示的局部变量 感谢您的建议 type Request struct id int line byt
  • 如何在运行“go test”时排除或跳过特定目录[重复]

    这个问题在这里已经有答案了 go test go list grep v vendor coverprofile testCoverage txt 我正在使用上述命令来测试文件 但有 1 个名为 Store 的文件夹我想从测试中排除 怎样才
  • Java 执行器和长寿命线程

    我继承了一些使用 Executors newFixedThreadPool 4 的代码运行 4 个长寿命线程来完成应用程序的所有工作 这是推荐的吗 我读过Java 并发实践 https rads stackoverflow com amzn
  • Openresty 中的并发模型是什么?

    我很难理解 openresty 或 nginx 的并发模型 我读了Lua变量作用域 http wiki nginx org HttpLuaModule Lua Variable Scope 它解释了变量的生命周期 但它没有说明对它们的并发访
  • 鸭子在 Go 中打字

    我想写一个Join函数接受任意对象String 方法 package main import fmt strings type myint int func i myint String string return fmt Sprintf
  • Golang 正则表达式命名组和子匹配

    我正在尝试匹配正则表达式并获取匹配的捕获组名称 当正则表达式仅与字符串匹配一次时 这是有效的 但如果它与字符串匹配多次 SubexpNames不返回重复的名称 这是一个例子 package main import fmt regexp fu
  • 断点会停止所有线程吗?

    如果我的程序中有两个线程同时运行 并在其中一个线程上设置了断点 那么当遇到此断点时 另一个线程也会停止 还是会继续执行 我用 Java 编写并使用 NetBeans 断点可以选择它们的行为方式 挂起单个线程或所有线程
  • 模板中的 bson.ObjectId

    我有一个具有 bson ObjectId 类型的结构 例如如下所示 type Test struct Id bson ObjectId Name string Foo string 我想在 html 模板中呈现它 Name Food a h
  • 打印到 stdout 会导致阻塞的 goroutine 运行吗?

    作为一个愚蠢的基本线程练习 我一直在尝试实现理发师睡觉的问题 http en wikipedia org wiki Sleeping barber problem在戈兰 对于通道来说 这应该很容易 但我遇到了一个 heisenbug 也就是
  • ConcurrentHashMap 内部是如何工作的?

    我正在阅读有关 Java 并发性的 Oracle 官方文档 我想知道Collection由返回 public static
  • 有队列实现吗?

    任何人都可以建议使用 Go 容器来实现简单快速的 FIF 队列 Go 有 3 种不同的容器 heap list and vector 哪一种更适合实现队列 事实上 如果您想要的是一个基本且易于使用的 fifo 队列 slice 可以满足您所
  • 正确的文件扩展名或缩写是什么。 golang 的文本/模板?

    我正在考虑为其创建语法荧光笔 但我不知道这种特定类型模板的常规缩写 In 例子之一 http golang org pkg text template example Template helpers从文本 模板 godoc 中 它们引用
  • 使用 Akka 1.3 的 actor 时,我需要注意生产者-消费者速率匹配吗?

    使用 Akka 1 3 时 我是否需要担心当生成消息的 Actor 生成消息的速度比使用消息的 Actor 的处理速度快时会发生什么 如果没有任何机制 在长时间运行的进程中 队列大小将增大以消耗所有可用内存 The doc http doc
  • 为什么 json.Unmarshal 返回映射而不是预期的结构?

    看看这个游乐场 http play golang org p dWku6SPqj5 http play golang org p dWku6SPqj5 基本上 我正在工作的图书馆收到了interface 作为参数 然后需要json Unma
  • 在 Gorilla Mux 中嵌套子路由器

    我一直在使用gorilla mux https github com gorilla mux满足我的路由需求 但我注意到一个问题 当我嵌套多个子路由器时它不起作用 这是示例 func main r mux NewRouter StrictS
  • select 语句是否保证通道选择的顺序?

    继从这个答案 https stackoverflow com a 25795236 274460 如果一个 goroutine 在两个通道上进行选择 是否保证通道的选择顺序与其发送的顺序相同 我对发送者是单线程的情况特别感兴趣 例如 是否保
  • 如何使用 GOPATH 的 Samba 服务器位置?

    我正在尝试将 GOPATH 设置为共享网络文件夹 当我进入 export GOPATH smb path to shared folder I get go GOPATH entry is relative must be absolute
  • 是否有一种更简单的方法可以并行运行命令,同时在 Windows PowerShell 中保持高效?

    此自我回答旨在为那些受困于 Windows PowerShell 并由于公司政策等原因而无法安装模块的用户提供一种简单且高效的并行替代方案 在 Windows PowerShell 中 built in可用的替代方案local并行调用是St

随机推荐