Go 语言之信号量

· Read in about 1 min · (204 Words)
dev golang

Go 信号

Go 语言中的信号量主要是用于Unix/Linux系统,但是也部份兼容Windows。 在标准库中,主要涉及到os/signalsyscall这两个包。

其中,os/signal包主要是提供信号操作函数,而syscall里定义了许多信号常量。

简单示例

这里先看一段简短的代码:

package main

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

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	s := <-c
	fmt.Println("Got signal:", s) // Got signal: interrupt
}

这里是Golang文档中的一个示例,程序会捕获os.Interrupt信号,也就是syscall.SIGINT信号。 当捕获到这个信号后,将其打印出来。运行这个示例时,按Ctrl+C中断程序运行,即可看到输出。

用法详解

os/signal包中,主要有以下几个函数:

// 忽略某个或某几个信号
func Ignore(sig ...os.Signal)
// 注册接收某个或某几个信号
func Notify(c chan<- os.Signal, sig ...os.Signal)
// 重置一个或多个信号处理,如果参数为空,则重置所有信号处理
func Reset(sig ...os.Signal)
// 停止接收信号
func Stop(c chan<- os.Signal)

接收处理信号很简单,只需要一个 channel 即可。当接收到信号后,就可以从 channel 中读取到, 然后就可以进行相应处理,与两个 Goroutine 通信类似。

用途示例

我们可以利用信号,实现一些动态功能(如不重启刷新加载配置等)。 在使用Nginx时,可以使用nginx -s reload来进行配置文件重载,而不中断服务。 这里我们使用Go语言信号量来模拟一下这个功能。

设计流程如下:

  1. 捕获发送给程序的syscall.SIGUSR1信号量
  2. 重新读取配置文件,刷新应用状态

代码如下:

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"
)

const (
	configFile = "config.txt"
)

func loadConfig() {
	bts, err := ioutil.ReadFile(configFile)
	if err != nil {
		// handle config file error
		fmt.Println(err.Error())
	} else {
		// do something with configurations
		fmt.Printf(string(bts))
	}
}

func listen() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGUSR1)
	for {
		<-c
		loadConfig()
	}
}

func main() {
	pid := os.Getpid()
	fmt.Printf("PID: %d\n", pid)
	go listen()
	loadConfig()
	for {
	}
}

运行(注意这里的两个Shell切换):

shell1>> $ ./example
shell1>> PID: 5555
shell1>> foo
shell2>> $ sudo kill -USR1 5555
shell1>> foo
shell2>> $ echo bar > ./config.txt
shell2>> $ sudo kill -USR1 5555
shell1>> bar

这里通过使用一个单独的goroutine来监听USR1信号量,如果捕获到了该信号, 就进行配置文件刷新,否则就一直监听。可以看到,当更改配置文件内容后, 发送USR1信号到程序,会引起程序重新加载配置文件,但不重启程序。

参考资料

Comments