我正在使用Go
中的控制台音乐播放器。每当用户选择并播放专辑时,我在播放列表上启动goroutine 到循环。
playlist := make([]*Media, 0)
for _, path := range album.Paths {
media, err := NewMediaFromPath(path)
// return err
playlist = append(playlist, media)
}
for idx := range playlist {
player.SetMedia(playlist[idx])
err = player.Play()
// check err
status, err := player.MediaState()
// check err
for status != MediaEnded && status != MediaStopped {
// update UI and check player status
// loop until the song finishes
status, err = player.MediaState()
}
}
当用户选择新专辑时,我需要一种取消此goroutine的方法。我正在使用context.Context
这样做(但我不相信这是最好的解决方案)。
我创建了ctx
ctx, cancel := context.WithCancel(context.Background())
因此,在UI事件处理程序中,play()
func
将cancel()
goroutine。
一旦我在更新UI for
循环中检查,这是有效的:
select {
case <-ctx.Done():
return err
default:
time.Sleep(50 * time.Millisecond)
}
然后关闭频道ctx.Done()
,播放的下一张专辑将始终返回而不是循环。
有没有办法重新计算context.Context
?
如果没有,有没有更好的方法来取消这个goroutine(以及以下goroutines)?
或者我尝试使用waitgroups,
go func() {
wg.Wait()
wg.Add(1)
err = playAlbum(
done,
player,
*albums[s],
list,
status,
)
wg.Done()
// handle err
}()
然后我得到sync: WaitGroup is reused before previous Wait has returned
恐慌
答案 0 :(得分:1)
如何使用频道取消goroutine?
select {
case <-chClose:
return
default:
}
你的cancel()电话可以简单地关闭频道:
close(chClose)
但是你不能再把它关闭了!所以你需要确保你的新专辑有一个新的chClose。根据您的代码结构,这可能是更清洁的解决方案。
或者你可以在chClose上发送一个值来启动停止例程:
chClose <- 1
你可以随心所欲地做到这一点。
请注意,如果没有goroutine监听,这将阻止(或者如果你有缓冲区,你将最终关闭尚未启动的例程。 - &gt;你需要一个干净的架构!)