1

我对 Go 还是很陌生,并且非常想弄清楚如何处理需要返回主循环的事件。

在 C 中,我可以只返回函数,但似乎我不能在这里这样做,至少用那个方法。

我可以处理需要在 sigHandler() 中处理的信号,即 SIGINT 或 SIGTERM,但在处理 SIGHUP 或 SIGUSR 时我需要将调用返回给 main。在我下面的代码中,一旦我发送了挂断信号,进程就停留在等待模式。

如果有人可以帮助我指导如何正确设计信号处理以处理需要返回到第一个 goroutine 中的主代码的调用,我将不胜感激。

编辑

我现在正在 select{} 中的主 goroutine 中处理通道消息,但是当我发送 HUP 信号时,下面的代码会退出。这里的目标是重新加载配置并继续正常执行。

在主行代码中,我有这个:

go sigHandler()
cs := make(chan bool, 1)
go sigHandler(cs)

// setup the http server
err := setupServer(addr, port)
if err != nil {
    fatal("Error setting up listening sockets")
    os.Exit(1)
}

    select {
    case quit := <-cs:
        if quit {
            logEvent(loginfo, sys, "Terminating..")
            closeLog()
            os.Exit(0)
        } else {
            logEvent(loginfo, sys, "Reloading configuration..")
            }
    }

函数 sigHandler()

func sigHandler(cs chan bool) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

    signal := <-c
    logEvent(lognotice, sys, "Signal received: "+signal.String())

    switch signal {
    case syscall.SIGINT, syscall.SIGTERM:
        cs <- true
    case syscall.SIGHUP:
            cs <- false
    }
}
4

2 回答 2

2

你可以这样做:

package main

import (
        "os"
        "os/signal"
        "syscall"
)

// We make sigHandler receive a channel on which we will report the value of var quit
func sigHandler(q chan bool) {
        var quit bool

        c := make(chan os.Signal, 1)
        signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

        // foreach signal received
        for signal := range c {
                //      logEvent(lognotice, sys, "Signal received: "+signal.String())

                switch signal {
                case syscall.SIGINT, syscall.SIGTERM:
                        quit = true
                case syscall.SIGHUP:
                        quit = false
                }

                if quit {
                        quit = false
                        //              closeDb()
                        //              logEvent(loginfo, sys, "Terminating..")
                        //              closeLog()
                        os.Exit(0)
                }
                // report the value of quit via the channel
                q <- quit
        }
}

func main() {
        // init two channels, one for the signals, one for the main loop
        sig := make(chan bool)
        loop := make(chan error)

        // start the signal monitoring routine
        go sigHandler(sig)


        // while vat quit is false, we keep going
        for quit := false; !quit; {
                // we start the main loop code in a goroutine
                go func() {
                        // Main loop code here
                        // we can report the error via the chan (here, nil)
                        loop <- nil
                }()

                // We block until either a signal is received or the main code finished
                select {
                // if signal, we affect quit and continue with the loop
                case quit = <-sig:
                // if no signal, we simply continue with the loop
                case <-loop:
                }
        }
}

但是,请注意,该信号将导致主循环继续,但不会停止第一个 goroutine 上的执行。

于 2013-09-20T08:02:47.667 回答
1

这是一种构建事物以做你想做的事情的方法,分离关注点,以便信号处理代码和主代码分开并且易于独立测试。

如何实现 Quit 和 ReloadConfig 完全取决于你的程序 - ReloadConfig 可能会在通道上向正在运行的 goroutine 发送“请重新加载”值;它可能会锁定一个互斥锁并更改一些共享的配置数据;或其他一些可能性。

package main

import (
    "log"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    obj := &myObject{}
    go handleSignals(obj)
    select {}
}

type myObject struct {
}

func (obj *myObject) Quit() {
    log.Printf("quitting")
    os.Exit(0)
}

func (obj *myObject) ReloadConfig() {
    log.Printf("reloading configuration")
}

type MainObject interface {
    ReloadConfig()
    Quit()
}

func handleSignals(main MainObject) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
    for sig := range c {
        switch sig {
        case syscall.SIGINT, syscall.SIGTERM:
            main.Quit()
            return
        case syscall.SIGHUP:
            main.ReloadConfig()
        }
    }
}
于 2013-09-20T10:10:43.813 回答