从List移除Player结构体后,如何彻底清理内存并终止goroutine循环
问题解决:移除Player实例并终止关联Goroutine
原代码核心问题
- Goroutine是无限循环,没有终止机制,即使Player实例从List中移除,Goroutine仍会持续运行
Create方法使用值接收者,返回的是函数内局部副本的指针,存在内存管理隐患- 手动调用
runtime.GC()无法强制立即回收,且Goroutine不终止的话,即使Player实例被回收,Goroutine依然独立运行
正确实现方案
通过context包管理Goroutine生命周期,给Player添加终止信号,确保移除实例时能同步终止Goroutine,最终让GC自动回收内存:
package main import ( "context" "container/list" "fmt" "time" ) type Player struct { PlayerID int ctx context.Context cancel context.CancelFunc } // NewPlayer 构造函数,创建Player并启动关联Goroutine func NewPlayer(playerID int) *Player { ctx, cancel := context.WithCancel(context.Background()) p := &Player{ PlayerID: playerID, ctx: ctx, cancel: cancel, } fmt.Println("Create Player:", p.PlayerID) go p.run() return p } // run Player关联的Goroutine逻辑,监听终止信号 func (p *Player) run() { for { select { // 收到终止信号时退出循环 case <-p.ctx.Done(): fmt.Println("Player", p.PlayerID, "goroutine exited") return default: println("test") time.Sleep(time.Second * 2) } } } // Stop 终止Player关联的Goroutine func (p *Player) Stop() { p.cancel() } func main() { playersList := list.New() playersList.PushFront(NewPlayer(1)) // 遍历链表时先保存下一个元素,避免Remove导致链表结构变化后遍历出错 for e := playersList.Front(); e != nil; { next := e.Next() player := e.Value.(*Player) println("Remove Player:", player.PlayerID) // 先终止Goroutine player.Stop() // 再从List中移除实例 playersList.Remove(e) e = next } fmt.Scanf("h") }
关键修改说明
- 使用
context.WithCancel创建可取消的上下文,绑定到Player实例,用于控制Goroutine生命周期 - 将原匿名Goroutine改为
run方法,通过select监听ctx.Done()信号,收到后立即退出循环 - 添加
Stop方法,调用cancel()触发终止信号,实现外部控制Goroutine退出 - 遍历链表时提前保存下一个元素,避免调用
Remove后当前元素的Next指针失效 - 移除实例前先终止Goroutine,确保后续Player实例无引用时能被GC自动回收
内容的提问来源于stack exchange,提问作者MaZa
相关产品推荐
相关产品推荐

