取消上下文。上下文不止一次?

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[s],
            list,
            status,
        )
        wg.Done()
        // handle err
     }()

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

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!!)