可以多次取消一个 context.Context 吗?

huangapple go评论183阅读模式
英文:

Cancel a context.Context more than once?

问题

我正在使用Go编写一个控制台音乐播放器。每当用户选择并播放一个专辑时,我会启动一个 goroutine循环播放列表。

  1. playlist := make([]*Media, 0)
  2. for _, path := range album.Paths {
  3. media, err := NewMediaFromPath(path)
  4. // 返回 err
  5. playlist = append(playlist, media)
  6. }
  7. for idx := range playlist {
  8. player.SetMedia(playlist[idx])
  9. err = player.Play()
  10. // 检查 err
  11. status, err := player.MediaState()
  12. // 检查 err
  13. for status != MediaEnded && status != MediaStopped {
  14. // 更新 UI 并检查播放器状态
  15. // 循环直到歌曲播放完毕
  16. status, err = player.MediaState()
  17. }
  18. }

我需要一种方法在用户选择新专辑取消这个 goroutine。我使用 context.Context 来实现(但我不确定这是否是最佳解决方案)。

我创建了 ctx

  1. ctx, cancel := context.WithCancel(context.Background())

因此,在 UI 事件处理程序中,play() 函数将调用 cancel() 函数来取消 goroutine。

这在更新 UI 的 for 循环中起作用:

  1. select {
  2. case <-ctx.Done():
  3. return err
  4. default:
  5. time.Sleep(50 * time.Millisecond)
  6. }

然后,通道 ctx.Done() 被关闭,接下来播放的专辑将始终返回而不是循环播放。

是否有一种方法可以重新取消 context.Context

如果没有,是否有更好的方法来取消这个 goroutine(以及后续的 goroutine)?

另外,我尝试使用了 waitgroups:

  1. go func() {
  2. wg.Wait()
  3. wg.Add(1)
  4. err = playAlbum(
  5. done,
  6. player,
  7. *albums[s],
  8. list,
  9. status,
  10. )
  11. wg.Done()
  12. // 处理 err
  13. }()

但是,我得到了一个 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.

  1. playlist := make([]*Media, 0)
  2. for _, path := range album.Paths {
  3. media, err := NewMediaFromPath(path)
  4. // return err
  5. playlist = append(playlist, media)
  6. }
  7. for idx := range playlist {
  8. player.SetMedia(playlist[idx])
  9. err = player.Play()
  10. // check err
  11. status, err := player.MediaState()
  12. // check err
  13. for status != MediaEnded &amp;&amp; status != MediaStopped {
  14. // update UI and check player status
  15. // loop until the song finishes
  16. status, err = player.MediaState()
  17. }
  18. }

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

  1. 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:

  1. select {
  2. case &lt;-ctx.Done():
  3. return err
  4. default:
  5. time.Sleep(50 * time.Millisecond)
  6. }

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,

  1. go func() {
  2. wg.Wait()
  3. wg.Add(1)
  4. err = playAlbum(
  5. done,
  6. player,
  7. *albums
    展开收缩
    ,
  8. list,
  9. status,
  10. )
  11. wg.Done()
  12. // handle err
  13. }()

But then I get a sync: WaitGroup is reused before previous Wait has returned panic

答案1

得分: 2

使用一个通道来取消goroutine怎么样?

  1. select {
  2. case <-chClose:
  3. return
  4. default:
  5. }

你的cancel()调用可以简单地关闭通道:

  1. close(chClose)

但是你不能再次关闭它!所以你需要确保你的新专辑有一个新的chClose。根据你的代码结构,这可能是更清晰的解决方案。

或者你可以只是在chClose上发送一个值来启动停止goroutine:

  1. chClose <- 1

你可以随时这样做。

请注意,如果没有goroutine在监听,这将阻塞(或者如果你有一个缓冲区,你将关闭甚至还没有开始的例程。-->你需要一个清晰的架构!!)

英文:

What about using a channel to cancel the goroutine?

  1. select {
  2. case &lt;-chClose:
  3. return
  4. default:
  5. }

Your cancel() call could simply close the channel:

  1. 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:

  1. chClose &lt;- 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!!)

huangapple
  • 本文由 发表于 2017年7月3日 03:29:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/44874456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定