goroutine не соблюдает `ctx.done ()` или выходит правильно - PullRequest
0 голосов
/ 30 марта 2019

Я пытаюсь изящно завершить работу, когда пользователь нажимает Ctrl-C.Я пытаюсь код в Сделайте Ctrl + C, чтобы отменить контекст. Context .основной пакет

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {

    ctx := context.Background()

    // trap Ctrl+C and call cancel on the context
    ctx, cancel := context.WithCancel(ctx)
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    defer func() {
        signal.Stop(c)
        cancel()
        fmt.Println("Cleaned up")
    }()
    go func() {
        select {
        case <-c:
            fmt.Println("Got interrupt signal")
            cancel()
        case <-ctx.Done():
        }
        fmt.Println("Stopped monitoring")
    }()
    select {
    case <-ctx.Done():
        fmt.Println("notified to quit")
    case <-time.NewTimer(time.Second * 2).C:
        fmt.Println("done something")
    }
}

Работает хорошо, как и ожидалось, когда пользователь нажимает Ctrl-c, выводит следующее:

Got interrupt signal
Stopped monitoring
notified to quit
Cleaned up

Однако, если он нормально завершается, он не работаеткак и ожидалось, как показано ниже:

done something
Cleaned up

Я имею в виду, что это должно распечатать Stopped monitoring, но не.В функции defer cleanup она вызывается cancel(), которая должна вызвать select in monitoring goroutine, чтобы выйти, но не.

Как решить проблему?

1 Ответ

0 голосов
/ 30 марта 2019

Спасибо @Zan Lynx, я разработал следующее решение.

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    terminated := monitor(ctx, cancel)
    defer func() {
        cancel()
        fmt.Println("Cleaned up")
        <-terminated // wait for the monior goroutine quit
    }()
    select {
    case <-ctx.Done():
        fmt.Println("notified to quit")
    case <-time.NewTimer(time.Second * 1).C:
        fmt.Println("done something")
    }
}

func monitor(ctx context.Context, cancel context.CancelFunc) <-chan interface{} {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    terminated := make(chan interface{})
    go func() {
        defer close(terminated)
        defer fmt.Println("Stopped monitoring1")
        defer signal.Stop(c)
        select {
        case <-c:
            fmt.Println("Got interrupt singnal")
            cancel()
        case <-ctx.Done():
        }
    }()
    return terminated
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...