Golang在sync.Map中操作对象

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

Golang manipulating object inside a sync.Map

问题

我正在尝试操作一个包含sync.Map的sync.Map的golang代码,但是在类型转换方面遇到了一些问题。

我有以下代码:

func (cluster *Cluster) action(object1, object2 MyObject) {
    value, _ := cluster.globalMap.LoadOrStore(object1.name, sync.Map{})
    localMap := value.(sync.Map)
    localMap.Store(object2.Name, object2)
    
    value2, _ := cluster.resourceInflight.Load(node.Name)
    forComparison := value2.(sync.Map)
    fmt.Println(localMap.Load(object2.Name))
    fmt.Println(forComparison.Load(object2.Name))
}
{myObject  map[] map[]} true
<nil> false

我这样做是因为我希望保持localMap的线程安全性。

问题是,我期望我的两个打印结果相同,因为"forComparison"应该指向与"localMap"相同的对象。但是第二个结果是nil。

我怀疑问题出在将接口"value"转换为实际的"sync.Map"时。但我不确定如何在内联转换的情况下调用.Store方法。

我考虑过将localMap存储在cluster.globalMap中,但我觉得这样做是不正确的,因为它会破坏使用localSyncMap的整个目的,并且会创建并发问题。

对于我应该怎么做,你有什么建议吗?

英文:

I am trying to manipulate a golang sync.Map of sync.Map, but I have some issues with the casting.
I have the following code:

func (cluster *Cluster) action(object1, object2 MyObject) {
				value, _ := cluster.globalMap.LoadOrStore(object1.name, sync.Map{})
				localMap := value.(sync.Map)
				localMap.Store(object2.Name, object2)
				
				value2, _ := cluster.resourceInflight.Load(node.Name)
				forComparison := value2.(sync.Map)
			    fmt.Println(localMap.Load(object2.Name))
				fmt.Println(forComparison.Load(object2.Name))
}
{myObject  map[] map[]} true
<nil> false

I am doing this since I wish to keep the content of localMap thread safe.

The problem is I am expecting to have the same result for my two print, as "forComparison" should be pointing to the same object than "localMap". But second result is nil.

I am suspecting that the problem is coming from the casting of the interface "value" into an actual "sync.Map". But I am not sure how I can actually call the .Store method with inline casting.

I thought about Storing localMap inside cluster.globalMap, but this seems incorrect to me as it would break the whole point of using a localSyncMap and create concurrency issues.

Any input on what I should do ?

答案1

得分: 2

根据评论所述,问题在于你复制了一个sync.Map。以下代码将失败(输出"Not found" - playground):

var sm sync.Map
var x interface{}
x = sm
sm2 := x.(sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
    fmt.Printf("Found: %s\n", result)
} else {
    fmt.Printf("Not found\n")
}

而使用指针则能按预期工作:

var sm sync.Map
var x interface{}
x = &sm
sm2 := x.(*sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
    fmt.Printf("Found: %s\n", result)
} else {
    fmt.Printf("Not found\n")
}

运行go vet可能会警告你其他问题(sync.Map包含一个sync.Mutex,这些"在首次使用后不能复制")。

请注意,Sync.Map的文档中指出:

Map类型是专用的。大多数代码应该使用普通的Go map,通过单独的锁定或协调来提供更好的类型安全性,并使维护与地图内容一起的其他不变量更容易。

英文:

As per the comments the issue was that you were copying a sync.Map; the following code will fail (output "Not found" - playground):

var sm sync.Map
var x interface{}
x = sm
sm2 := x.(sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
	fmt.Printf("Found: %s\n", result)
} else {
	fmt.Printf("Not found\n")
}

Whereas using a pointer works as expected:

var sm sync.Map
var x interface{}
x = &sm
sm2 := x.(*sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
	fmt.Printf("Found: %s\n", result)
} else {
	fmt.Printf("Not found\n")
}

Running go vet would probably have warned you about other issues (sync.Map contains a sync.Mutex and these "must not be copied after first use").

Note that the docs for Sync.Map state:

>The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

huangapple
  • 本文由 发表于 2022年2月21日 10:31:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/71200609.html
匿名

发表评论

匿名网友

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

确定