Как приостановить и возобновить горутин? - PullRequest
0 голосов
/ 02 марта 2020

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

Ответы [ 2 ]

5 голосов
/ 02 марта 2020

A select с несколькими готовыми кейсами выбирает один псевдослучайно. Таким образом, если программа «медленно» проверяет эти каналы, вы можете отправить значение как на pause, так и на resume (при условии, что они буферизированы), так что прием от обоих каналов может быть готов, и сначала можно выбрать resume и на более поздней итерации pause, когда выполнение программы больше не должно приостанавливаться.

Для этого вы должны использовать переменную "state", синхронизированную мьютексом. Примерно так:

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 Детская площадка ):

Running
Running
Running
Paused
Paused
Paused
Running
Running
1 голос
/ 02 марта 2020

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

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

...