Post

指针本身的同步问题

指针本身的同步问题

问题发生的场景

用户可能有两个不同的结构体或变量,每个都保存着自己的指针,现在需要这两个指针变量本身在修改时保持同步。这种情况下,如何让两个独立的变量在修改时互相通知并更新?

两个指针本身被修改了,比如重新指向其他地方,这样就会导致两个内存块中的指针不再同步。比如,假设内存块A和B各自保存一个指针,指向同一个数据对象。如果通过内存块A的指针修改了数据,那么通过B的指针访问时,数据应该已经改变,这没问题。但如果其中一个指针被重新指向另一个地址,另一个指针并不会跟着变,这时候两个内存块中的指针就不一致了。

问题分析

我们在很多场景下都会遇到,多个结构体的定义中包含相同类型的结构体指针。如下:

1
2
3
4
5
6
7
8
9
type A struct {
    c *C
}
type B struct {
    c *C
}
type C struct {
    ...
}

假设有这样的情况,我们需要用B的结构体来进行某些操作,会使用到B的c指针指向的内容。B的指针是通过A赋值而来的,自然而然想要与A的c指针保持一致。假设有下面的方法,读者可以思考下调用它会导致什么问题。

1
2
3
4
5
6
7
8
b := &B{c: a.c} // 这里的a.c是指向C的指针
func (a *A) UpdateC(updateC *C) {
    a.c = updateC
}
UpdateC(c)
Use(b.c)
UpdateC(nil)
Use(b.c)

没错,此时调用的Use方法里的c是落后的,和a不一致的,会导致出现错误。甚至我们将指针置为空了,此时画面表现应该是空的,但是用B的成员发现还是有数据的,这就是很严重的错误了。

那么如何解决这个问题呢?

其实这个问题和大部分值拷贝问题一样,都是因为指针本身的值被拷贝了,值拷贝产生的问题同样存在在指针拷贝上。

解决方案

二级指针

如果我们持有的是指针的指针,那么改变内部指针值,就可以通过不变的二级指针来同步访问到变化的内部指针值了。下面以玩家的装备信息为例(其实是本人工作中遇到的问题,具体点方便读者理解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type EquiptState struct {
    equiptProp *Prop
}

type GameState struct {
    userId int
    equiptState *EquiptState
}

type Bag struct {
    usersEquiptStates map[string]*EquiptState
}

func (b *Bag) UpdateEquiptState(userId string, updateProp *Prop) {
    b.usersEquiptStates[userId].equiptProp = updateProp
}

func GenerateGameState(userId string, bag * Bag) *GameState {
    return &GameState{
        userId: userId,
        equiptState: bag.usersEquiptStates[userId],
    }
}

这样哪怕我们在背包模块中更新玩家的装备状态,发送的快照信息也会实时变化,这样就不会出现指针不一致的问题了。

串行的一致性问题通过指针解决、并行的一致性问题通过锁或原子操作解决。这个理念要牢记于心。

This post is licensed under CC BY 4.0 by the author.