【问题标题】:How to pause and resume goroutine?如何暂停和恢复 goroutine?
【发布时间】:2020-06-14 19:32:02
【问题描述】:

我正在尝试暂停和恢复 groutine。我知道我可以sleep 运行,但我正在寻找的是一个按钮“暂停/恢复”而不是一个计时器。

这是我的尝试。我正在使用通道的阻塞功能来暂停,select 根据通道值切换要执行的内容。然而,在我的例子中,输出总是Running

func main() {
    ctx := wctx{}
    go func(ctx wctx) {
        for {
            time.Sleep(1 * time.Second)
            select {
            case <-ctx.pause:
                fmt.Print("Paused")
                <-ctx.pause
            case <-ctx.resume:
                fmt.Print("Resumed")
            default:
                fmt.Print("Running \n")
            }
        }
    }(ctx)

    ctx.pause <- struct{}{}
    ctx.resume <- struct{}{}
}

type wctx struct {
    pause  chan struct{}
    resume chan struct{}
}

【问题讨论】:

  • 只有一种方法可以暂停和恢复 goroutine,但此功能存在于运行时 github.com/golang/go/blob/master/src/runtime/… 中。否则,您可以尝试双重锁定和双重解锁互斥锁。通常,您不想这样做。
  • 听起来像是 XY 问题。

标签: go concurrency channel goroutine


【解决方案1】:

具有多个就绪案例的select 随机选择一个。因此,如果 goroutine 检查这些通道的速度“很慢”,您可能会在 pauseresume 上都发送一个值(假设它们是缓冲的),因此可以准备好从两个通道接收,并且可以首先选择 resume ,在以后的迭代中,当 goroutine 不应再暂停时,pause

为此,您应该使用由互斥锁同步的“状态”变量。像这样的:

const (
    StateRunning = iota
    StatePaused
)

type wctx struct {
    mu    sync.Mutex
    state int
}

func (w *wctx) SetState(state int) {
    w.mu.Lock()
    defer w.mu.Unlock()
    w.state = state
}

func (w *wctx) State() int {
    w.mu.Lock()
    defer w.mu.Unlock()
    return w.state
}

测试它:

ctx := &wctx{}
go func(ctx *wctx) {
    for {
        time.Sleep(1 * time.Millisecond)
        switch state := ctx.State(); state {
        case StatePaused:
            fmt.Println("Paused")
        default:
            fmt.Println("Running")
        }
    }
}(ctx)

time.Sleep(3 * time.Millisecond)
ctx.SetState(StatePaused)
time.Sleep(3 * time.Millisecond)
ctx.SetState(StateRunning)
time.Sleep(2 * time.Millisecond)

输出(在Go Playground 上试试):

Running
Running
Running
Paused
Paused
Paused
Running
Running

【讨论】:

    【解决方案2】:

    你需要初始化你的通道,记住从 nil 通道读取总是阻塞的。

    selectdefault 案例永远不会阻塞。

    这是您的程序的修改版本,修复了上述问题:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        ctx := wctx{
            pause:  make(chan struct{}),
            resume: make(chan struct{}),
        }
    
        go func(ctx wctx) {
            for {
                select {
                case <-ctx.pause:
                    fmt.Println("Paused")
                case <-ctx.resume:
                    fmt.Println("Resumed")
                }
    
                fmt.Println("Running")
                time.Sleep(time.Second)
            }
        }(ctx)
    
        ctx.pause <- struct{}{}
        ctx.resume <- struct{}{}
    }
    
    type wctx struct {
        pause  chan struct{}
        resume chan struct{}
    }
    
    

    【讨论】:

    • 你能解释一下为什么我需要在这里通过引用传递吗?我想我没有修改结构的任何值,所以我不需要?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    • 2013-07-19
    • 2015-07-28
    相关资源
    最近更新 更多