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