多次取消context.Context会发生什么?

5

我正在使用 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() 函数取消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 的恐慌

1个回答

2
使用通道来取消goroutine怎么样?
select {
case <-chClose:
    return
default:
}

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

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

或者,你只需发送一个值到chClose来启动go例程的停止:

chClose <- 1

你可以随意多次执行此操作。
请注意,如果没有goroutine在监听,这将会阻塞(或者如果你有一个缓冲区,那么你将会关闭甚至还没有开始的例程。--> 你需要一个清晰的架构!!)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接