英文:
Cancel a context.Context more than once?
问题
我正在使用Go
编写一个控制台音乐播放器。每当用户选择并播放一个专辑时,我会启动一个 goroutine 来循环播放列表。
playlist := make([]*Media, 0)
for _, path := range album.Paths {
media, err := NewMediaFromPath(path)
// 返回 err
playlist = append(playlist, media)
}
for idx := range playlist {
player.SetMedia(playlist[idx])
err = player.Play()
// 检查 err
status, err := player.MediaState()
// 检查 err
for status != MediaEnded && status != MediaStopped {
// 更新 UI 并检查播放器状态
// 循环直到歌曲播放完毕
status, err = player.MediaState()
}
}
我需要一种方法在用户选择新专辑时取消这个 goroutine。我使用 context.Context
来实现(但我不确定这是否是最佳解决方案)。
我创建了 ctx
ctx, cancel := context.WithCancel(context.Background())
因此,在 UI 事件处理程序中,play()
函数将调用 cancel()
函数来取消 goroutine。
这在更新 UI 的 for
循环中起作用:
select {
case <-ctx.Done():
return err
default:
time.Sleep(50 * time.Millisecond)
}
然后,通道 ctx.Done()
被关闭,接下来播放的专辑将始终返回而不是循环播放。
是否有一种方法可以重新取消 context.Context
?
如果没有,是否有更好的方法来取消这个 goroutine(以及后续的 goroutine)?
另外,我尝试使用了 waitgroups:
go func() {
wg.Wait()
wg.Add(1)
err = playAlbum(
done,
player,
*albums[s],
list,
status,
)
wg.Done()
// 处理 err
}()
但是,我得到了一个 sync: WaitGroup is reused before previous Wait has returned
的 panic 错误。
英文:
I'm working on a console music player in Go
. Whenever the user selects and plays an album, I launch a goroutine to loop over a playlist.
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()
}
}
I need a way of canceling this goroutine when the user selects a new album. I'm using context.Context
to do so (but I'm not convinced it's the best solution).
I create the ctx
ctx, cancel := context.WithCancel(context.Background())
So in the UI event handler, the play()
func
will cancel()
the goroutine.
This works once I check inside the update UI for
loop:
select {
case <-ctx.Done():
return err
default:
time.Sleep(50 * time.Millisecond)
}
Then the channel ctx.Done()
is closed and the next albums played will always return instead of loop.
Is there a way to recancel a context.Context
?
If not, is there a better way to cancel this goroutine (and the following goroutines)?
Alternatively I've tried to use waitgroups,
go func() {
wg.Wait()
wg.Add(1)
err = playAlbum(
done,
player,
*albums展开收缩,
list,
status,
)
wg.Done()
// handle err
}()
But then I get a sync: WaitGroup is reused before previous Wait has returned
panic
答案1
得分: 2
使用一个通道来取消goroutine怎么样?
select {
case <-chClose:
return
default:
}
你的cancel()调用可以简单地关闭通道:
close(chClose)
但是你不能再次关闭它!所以你需要确保你的新专辑有一个新的chClose。根据你的代码结构,这可能是更清晰的解决方案。
或者你可以只是在chClose上发送一个值来启动停止goroutine:
chClose <- 1
你可以随时这样做。
请注意,如果没有goroutine在监听,这将阻塞(或者如果你有一个缓冲区,你将关闭甚至还没有开始的例程。-->你需要一个清晰的架构!!)
英文:
What about using a channel to cancel the goroutine?
select {
case <-chClose:
return
default:
}
Your cancel() call could simply close the channel:
close(chClose)
but then you cannot close it again! So you need to make sure your new album has a new chClose. Depending on your code structure this might be the cleaner solution.
Alternatively you can just send a value on chClose to initiate a stop of the go routine:
chClose <- 1
You can do that as often as you want.
Note that if there is no goroutine listening, this will block (or if you have a buffer, you will end up closing routines that have not even startet yet. --> You need a clean architecture!!)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论