Go:如何从切片中删除元素并在内存中修改它

时间:2016-07-09 10:12:44

标签: go struct slice side-effects

我正在尝试编写一个简单的服务器/客户端聊天程序用于学习目的,但我被困住了。我希望Leave函数删除它传递的指针并更新结构中的切片,以便指针不再存在。但它不起作用。

示例:InputOutput

type Room struct {
    Name     string
    Visitors []*net.Conn
} 


func (r *Room) Leave(pc *net.Conn) {
    for i, pv := range r.Visitors {
        //found the connection we want to remove
        if pc == pv {
            fmt.Printf("Before %v\n",r.Visitors)
            r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
            fmt.Printf("Before %v\n",r.Visitors)                
            return
        }
    }
}

2 个答案:

答案 0 :(得分:2)

删除逻辑是正确的(您的输出也证实了这一点)。问题是多个goroutines之间缺乏同步(你的问题没有说明这一点)。

您说(在您的评论中)您希望首先使用1 goroutine,然后再处理同步。但是你的代码已经使用了多个goroutine,所以你不能拥有这样的奢侈品:

//Let a goroutine Handle the connection
go handleConnection(conn)

多个goroutine正在读取和写入Room.Visitors切片,因此您别无选择,只能同步对它的访问。

一个例子是使用sync.RWLock

utils.go

type Room struct {
    Name     string
    Visitors []net.Conn
    mux      sync.RWLock
}


func (r *Room) Leave(c net.Conn) {
    r.mux.Lock()
    defer r.mux.Unlock()
    for i, v := range r.Visitors {
        //found the connection we want to remove
        if c == v {
            fmt.Printf("Before remove %v\n", r.Visitors)
            r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
            fmt.Printf("After remove %v\n", r.Visitors)
            return
        }
    }
}

此外,只要其他任何代码触及Room.Visitors,也会锁定代码(如果您只是阅读代码,可以使用RWMutex.RLock()。)

同样,您需要同步访问由多个goroutine读取/更改的所有变量。

还要考虑清除删除后释放的元素,否则底层数组仍会保留该值,从而阻止gargabe收集器正确释放它使用的内存。有关此内容的详细信息,请参阅Does go garbage collect parts of slices?

答案 1 :(得分:0)

您正在使用指向Visitors []*net.Conn类型中的界面(Room)的指针。您永远不需要指向接口值的指针。接口的值类似于通用内容持有者,其内存表示形式与实现该接口的结构不同。

您应该简单地使用Visitors类型声明为接口:

type Room struct {
    Name     string
    Visitors []net.Conn // should not be a pointer to interface value
} 


func (r *Room) Leave(pc net.Conn) { // same thing as above
    for i, pv := range r.Visitors {
        // found the connection we want to remove
        // in the comparison below actual concrete types are being compared.
        // both these concrete types must be comparable (they can't be slices for example
        if pc == pv {
            r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
            return
        }
    }
}

请注意,上面的比较(pc == pv)并非如此微不足道。在此处阅读:https://golang.org/ref/spec#Comparison_operators

另请参阅此问题:Why can't I assign a *Struct to an *Interface?