多次取消上下文。上下文?

问题描述 投票:0回答:2

我正在

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(以及后面的goroutine)?

或者我尝试使用等待组,

    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
恐慌

go goroutine cancellation tui
2个回答
2
投票

使用通道取消goroutine怎么样?

select {
case <-chClose:
    return
default:
}

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

close(chClose)

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

或者,您可以在 chClose 上发送一个值来启动 go 例程的停止:

chClose <- 1

您可以根据需要多次这样做。

请注意,如果没有 goroutine 监听,这将阻塞(或者如果你有缓冲区,你最终将关闭尚未启动的例程。--> 你需要一个干净的架构!!)


0
投票

您也可以包装上下文,如果这对您的用例有帮助的话:

https://go.dev/play/p/XvvitAmhkVt

package main

import (
    "context"
    "fmt"
)

func IsCanceled(ctx context.Context) bool {
    return ctx.Err() != nil
}

func main() {
    a, cancelA := context.WithCancel(context.Background())
    b, cancelB := context.WithCancel(a)
    c, cancelC := context.WithCancel(b)

    fmt.Println(IsCanceled(a), IsCanceled(b), IsCanceled(c))
    cancelB()
    fmt.Println(IsCanceled(a), IsCanceled(b), IsCanceled(c))
    cancelA()
    fmt.Println(IsCanceled(a), IsCanceled(b), IsCanceled(c))
    cancelC()
    fmt.Println(IsCanceled(a), IsCanceled(b), IsCanceled(c))
}
© www.soinside.com 2019 - 2024. All rights reserved.