Как приостановить и возобновить горутину?

Я пытаюсь приостановить и возобновить раствор. Я понимаю, что могу 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{}
}

person Jill Clover    schedule 02.03.2020    source источник
comment
Есть только один способ приостановить и возобновить горутину, но эта функциональность находится внутри среды выполнения github.com/golang/go/blob/master/src/runtime/. В противном случае вы можете попробовать двойную блокировку и двойную разблокировку мьютекса. Обычно вы не хотите этого делать.   -  person Laevus Dexter    schedule 02.03.2020
comment
Похоже на проблему XY.   -  person Volker    schedule 02.03.2020


Ответы (2)


select с несколькими готовыми кейсами выбирает один псевдослучайно. Таким образом, если горутина «медленно» проверяет эти каналы, вы можете отправить значение как по pause, так и по resume (при условии, что они буферизированы), чтобы можно было принимать сигналы из обоих каналов, и сначала можно было бы выбрать resume, а в более поздней итерации. 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
person icza    schedule 02.03.2020

Вам необходимо инициализировать свои каналы, помните, что чтение из нулевых каналов всегда блокируется.

select с default регистром никогда не блокируется.

Вот модифицированная версия вашей программы, которая устраняет вышеупомянутые проблемы:

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{}
}

person Shangjian Ding    schedule 02.03.2020
comment
Вы можете объяснить, зачем мне здесь проходить по ссылке? Я думаю, что я не изменяю какое-либо значение структуры, поэтому мне это не нужно? - person Jill Clover; 02.03.2020