【问题标题】:Go signal handling去信号处理
【发布时间】:2013-09-20 04:14:32
【问题描述】:

我对 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
    }
}

【问题讨论】:

  • 您到底想在SIGHUP 上做什么?大多数 Go 程序实际上并没有主循环的概念,但您可以通过另一个通道轻松地在 goroutine 之间传递通知。
  • 您应该熟悉频道,恕我直言。他们做你想做的事。
  • @JamesHenstridge 我在主处理和信号处理之间添加了一个通道,但它似乎不起作用。

标签: go signals


【解决方案1】:

你可以这样做:

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 上的执行。

【讨论】:

    【解决方案2】:

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

    如何实现 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()
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 2010-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多