复现 “fatal error: concurrent map read and map write” 错误。

huangapple go评论77阅读模式
英文:

reproduce "fatal error: concurrent map read and map write"

问题

我正在调试一个程序,出现了一个名为fatal error: concurrent map read and map write的错误。这个程序可以简化为以下代码:

package main

import (
	"sync"
	"time"
)

func read(channelMap *map[int]chan bool, key int, mutex *sync.Mutex) {
	mutex.Lock()
	(*channelMap)[key] = make(chan bool, 1)
	mutex.Unlock()

	defer func() {
		mutex.Lock()
		delete((*channelMap), key)
		mutex.Unlock()
	}()

	select {
	case <-(*channelMap)[key]:
		{
		}
	case <-time.After(time.Second):
		{
		}
	}
}

func write(channelMap *map[int]chan bool, key int, mutex *sync.Mutex) {
	mutex.Lock()
	if channel, exist := (*channelMap)[key]; exist {
		channel <- true
	}
	mutex.Unlock()
}

func main() {
	mutex := &sync.Mutex{}
	channelMap := make(map[int]chan bool)

	for i := 0; i < 100; i++ {
		go func() {
			for {
				read(&channelMap, 0, mutex)
				time.Sleep(time.Millisecond)
			}
		}()
		go func() {
			for {
				write(&channelMap, 0, mutex)
				time.Sleep(time.Millisecond)
			}
		}()
	}

	time.Sleep(10 * time.Minute)
}

在这个程序中,只有channelMapmutex在不同的函数中被共享,其他所有内容都是参数化的。

有多个readwrite函数,它们都可以访问存储channelchannelMapread函数会等待从特定的通道接收一个值,等待的时间为给定的时间(在示例代码中为1秒),而write函数会在通道存在于channelMap中时向该通道发送一个值。

channelMap的每次访问都受到共享的sync.Mutex的保护,唯一的例外是:

	select {
	case <-(*channelMap)[key]:
		{
		}
	case <-time.After(time.Second):
		{
		}
	}

我认为这是唯一的漏洞,但是即使使用简化的程序,我仍然无法重现这个错误。有人能解释一下这个程序可能存在的逻辑缺陷吗?

英文:

I'm debugging my program for a bug fatal error: concurrent map read and map write. The program can be simplified as:

package main
import (
&quot;sync&quot;
&quot;time&quot;
)
func read(channelMap *map[int]chan bool, key int, mutex *sync.Mutex) {
mutex.Lock()
(*channelMap)[key] = make(chan bool, 1)
mutex.Unlock()
defer func() {
mutex.Lock()
delete((*channelMap), key)
mutex.Unlock()
}()
select {
case &lt;-(*channelMap)[key]:
{
}
case &lt;-time.After(time.Second):
{
}
}
}
func write(channelMap *map[int]chan bool, key int, mutex *sync.Mutex) {
mutex.Lock()
if channel, exist := (*channelMap)[key]; exist {
channel &lt;- true
}
mutex.Unlock()
}
func main() {
mutex := &amp;sync.Mutex{}
channelMap := make(map[int]chan bool)
for i := 0; i &lt; 100; i++ {
go func() {
for {
read(&amp;channelMap, 0, mutex)
time.Sleep(time.Millisecond)
}
}()
go func() {
for {
write(&amp;channelMap, 0, mutex)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(10 * time.Minute)
}

For this program, only channelMap and mutex are shared in different functions, everything else is parameterized.

There are multiple read and write, who have access to channelMap, a map storing channel. read waits for a value from a certain channel for some given time (1 second in the example code), and write send a value into the same channel when this channel exist in the channelMap.

Every access to channelMap has been protected by a shared sync.Mutex and the only exception is

	select {
case &lt;-(*channelMap)[key]:
{
}
case &lt;-time.After(time.Second):
{
}
}

I think this is the only vulnerability but with the simplified program I still couldn't reproduce the bug. Can someone explain to me the possible logical flaw in this program?

答案1

得分: 1

当同时修改数据结构时,所有的读写操作都必须由互斥锁保护。

这部分代码:case &lt;-(*channelMap)[key]: 在没有持有锁的情况下访问了channelMap

对数据结构的并发访问可能存在两种情况:

  • 零个写者,一个或多个读者;
  • 一个写者,零个读者。
英文:

When modifying a data structure concurrently, all reads and writes must be protected by a mutex.

This part: case &lt;-(*channelMap)[key]: accesses channelMap without holding a lock.

Concurrent access to a data structure is possible in two situations:

  • Zero writers, one or more readers, or
  • One writer, zero readers.

huangapple
  • 本文由 发表于 2021年6月20日 22:29:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/68056832.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定