前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang语言情怀-第40期 Go 语言设计模式 信号

Golang语言情怀-第40期 Go 语言设计模式 信号

作者头像
李海彬
发布于 2021-03-09 02:55:00
发布于 2021-03-09 02:55:00
67100
代码可运行
举报
文章被收录于专栏:Golang语言社区Golang语言社区
运行总次数:0
代码可运行

1、使用场景

实际项目中,我们希望修改了配置文件后,但又不想通过重启进程让它重新加载配置文件,可以使用signal的方式进行信号传递,或者我们希望通过信号控制,实现一种优雅的退出方式。Golang为我们提供了signal包,实现信号处理机制,允许Go 程序与传入的信号进行交互。

2、常用的Term信号

3、简单的栗子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

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

func main() {
        c := make(chan os.Signal)
        signal.Notify(c)
        fmt.Println("start..")
        s := <-c
        fmt.Println("End...", s)
}

(1)传递SIGINT信号

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[homework@xxxxx signal]$ go run monitor.go
start..




#此时,CTL+C发送一个SIGINT信号量,得到输出为:
[homework@xxxxx signal]$ go run monitor.go
start..
^CEnd... interrupt

(2)传递SIGTERM信号

打开2个Term窗口 第一个运行go run monitor.go程序 第二个执行:ps -ef | grep monitor.go | grep grep -v | awk '{print $2}' | xargs kill

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#此时,kill命令发送一个SIGTERM信号量,得到输出为:
[homework@xxxxx signal]$ go run monitor.go
start..
Terminated

4、优雅的退出守护进程

(1)何为优雅(graceful)?

Linux Server端的应用程序经常会长时间运行,在运行过程中,可能申请了很多系统资源,也可能保存了很多状态。

在这些场景下,我们希望进程在退出前,可以释放资源或将当前状态dump到磁盘上或打印一些重要的日志,即希望进程优雅退出。

(2)从对优雅退出的理解不难看出:优雅退出可以通过捕获SIGTERM来实现。

A、注册SIGTERM信号的处理函数并在处理函数中做一些进程退出的准备,信号处理函数的注册sigaction()来实现。

B、在主进程的main()中,通过类似于while(!fQuit)的逻辑来检测那个flag变量,一旦fQuit在signal handler function中被置为true,则主进程退出while()循环,接下来就是一些释放资源或dump进程当前状态或记录日志的动作,完成这些后,主进程退出。

栗子:优雅退出go守护进程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

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

func main() {
        //创建监听退出chan
        c := make(chan os.Signal)
        //监听指定信号 ctrl+c kill
        signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, 
                         syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)
        go func() {
                for s := range c {
                        switch s {
                        case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
                                fmt.Println("Program Exit...", s)
                                GracefullExit()
                        case syscall.SIGUSR1:
                                fmt.Println("usr1 signal", s)
                        case syscall.SIGUSR2:
                                fmt.Println("usr2 signal", s)
                        default:
                                fmt.Println("other signal", s)
                        }
                }
        }()

        fmt.Println("Program Start...")
        sum := 0
        for {
                sum++
                fmt.Println("sum:", sum)
                time.Sleep(time.Second)
        }
}

func GracefullExit() {
        fmt.Println("Start Exit...")
        fmt.Println("Execute Clean...")
        fmt.Println("End Exit...")
        os.Exit(0)
}

执行程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[homework@xxxx signal]$ go run monitor.go
Program Start...
sum: 1
sum: 2
sum: 3
sum: 4
sum: 5
sum: 6
^CProgram Exit... interrupt
Start Exit...
Execute Clean...
End Exit...

5、信号的订阅

信号的订阅是通过 channel实现的,每个os.Signal channel 都会收听自己相应的事件集。

关于Golang之信号处理的文章就写到这里,Done!

信号量

2.1 共享变量

在理解信号量之前,先了解采用共享变量使用多线程会出现什么问题。下面是一个C代码片段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1for (i=0; i<niters; i++){
2    cnt ++;
3}

cnt为全局变量,一个线程执行该代码片段的时候的汇编代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1   movq (%rdi), %rcx
 2    testq %rcx, %rcx
 3    jle .L2
 4    movl $0, %eax
 5.L3:
 6    movq cnt(%rip), %rdx
 7    addq %eax
 8    movq %eax, cnt(%rip)
 9    addq $1, %rax
10    cmpq %rcx, %rax
11    jne .L3
12.L2

2.2 信号量其中6-8行分别对应对应着加载cnt,更新cnt和存储cnt。将cnt变量从内存位置读出,加载到CPU寄存器中,在CPU运算器中加1,然后存储到cnt的内存位置。虽然代码中cnt++只有一行,但是转换为汇编代码的时候不只有一个操作,也就是说该语句不是原子操作。如果多个线程同时执行代码,按照之前的条件,不对CPU的执行顺序做任何假设,如果其中线程a在执行7行汇编代码,而线程b执行6行汇编代码,那么b将"看不到"线程a对全局变量cnt加1的操作,那么每次执行的结果cnt也不完全一致。

计算机领域先驱Dijkstra提出经典的解决上述问题的方法:信号量(semaphore)。它是一个非负整数的全局变量。而且该变量只能有两个特殊操作来处理: PV

  • P(s):如果s非零,那么Ps1,并且立即返回。如果s为零,那么就挂起这个线程,知道s为非零。
  • V(s): V操作将s1。如果有任何线程阻塞在P操作等待s非零,那么V将重启其中线程中的一个。

Posix标准定义需要操作信号量的函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1#include <semaphore.h>
2int sem_init(sem_t *sem, 0, unsigned int value);
3int sem_wait(sem_t *s); /*P(s)*/
4int sem_post(sem_t *s); /*P(s)*/

那么如何使用信号量是的2.1小节出现同步问题解决呢?首先定义全局信号量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1volatile long cnt = 0; /* global variable */
2sem_t mutex; /*global semaphore*/

初始化信号量,在这里初始值为1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1sem_init(&mutex, 0, 1);

最后使用信号量操作函数将临界区域代码包含起来

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1for (i =0; i<niters; i++){
2    sem_wait(&mutex);
3    cnt++;
4    sem_post(&mutex);
5}

参考资料:

Go语言信号处理

https://zhuanlan.zhihu.com/p/128953024

信号量,锁和 golang 相关源码分析

https://cloud.tencent.com/developer/article/1357636

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言情怀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go 语言优雅退出:让程序体面“退休”
在 Go 语言开发中,如何让程序优雅地退出是个绕不开的话题。无论是 Web 服务器、后台任务,还是微服务架构,程序总有终止的时候。如果不做好资源清理,可能会带来数据丢失、任务中断等一系列问题。今天,我们就来聊聊 Go 语言中的优雅退出,看看如何让你的程序从容退场,而不是“摔门而去”。
FunTester
2025/03/04
890
Go 语言优雅退出:让程序体面“退休”
使用信号进制实现进程通信
而在应用系统开发中,我们常用的方式就是消息队列和套接字两种方式。在程序中写了一个死循环,运行时,常使用 ctrl+c来中断进程。突然软件卡死了,我们无法关闭,这时,你知道使用kill -9 pip来结束进程。这些基本的操作常识性操作,背后就使用的“信号量"和应用程序发生通信。
暮雨
2019/08/21
7380
容器应用优雅关闭的终极大招
优雅关闭:在关闭前,执行正常的关闭过程,释放连接和资源,如我们操作系统执行 shutdown。
米开朗基杨
2021/08/10
2.8K0
GO 语言的并发模式你了解多少?
实际上,出现上述的情况,还是因为我们对于 GO 语言的并发模型和涉及的 GO 语言基础不够扎实,误解了语言的用法。
阿兵云原生
2023/10/24
3630
GO 语言的并发模式你了解多少?
Go:Signal信号量的简介与实践(优雅的退出)
有些信号名对应着3个信号值,这是因为这些信号值与平台相关,SIGKILL和SIGSTOP这两个信号既不能被应用程序捕获,也不能被操作系统阻塞或忽略。
Freedom123
2024/03/29
5150
Go:Signal信号量的简介与实践(优雅的退出)
使用信号进制实现进程通信
而在应用系统开发中,我们常用的方式就是消息队列和套接字两种方式。在程序中写了一个死循环,运行时,常使用<code>ctrl+c</code>来中断进程。突然软件卡死了,我们无法关闭,这时,你知道使用kill -9 pip来结束进程。这些基本的操作常识性操作,背后就使用的“信号量"和应用程序发生通信。
暮雨
2019/08/19
1.1K0
golang使用信号量热更新
package main import ( "encoding/json" "fmt" "io/ioutil" "log" "os" "os/signal" "sync" "syscall" ) //用json配置测试 type Config struct { Test1 string `json:"Test1:` Test2 int `json:"Test1
李海彬
2018/03/26
1.7K0
Go每日一库之112:asynq
Asynq是一个go语言实现的分布式任务队列和异步处理库,基于redis,类似sidekiq和celery,他具有以下特点:
luckpunk
2023/09/30
1.4K0
[Go] gocron源码阅读-groutine与channel应用到信号捕获
直接使用go 函数名()可以开启一个grountine,channel可以接收信息并且如果没有数据时会阻塞住 channel对应的是底层数据结构的引用,复制channel和函数传参都是拷贝的引用 make的时候第二个参数是1,就表示是有缓存的channel
唯一Chat
2019/11/18
5450
Go程序例子(82):信号
有时候我们希望我们的Go程序能够智能地处理Unix信号。例如,我们可能希望服务器在接收到SIGTERM时能够优雅地关闭,或者命令行工具在接收到SIGINT时停止处理输入。以下是如何在Go中使用通道处理信号的方法。
用户11078756
2025/01/21
550
Go程序例子(82):信号
Golang深入浅出之-信号(Signals)处理与优雅退出Go程序
在Go语言编程中,处理操作系统发送给进程的信号(Signals)是实现程序优雅退出、响应外部中断请求等关键功能的重要手段。本文将深入浅出地介绍Go中信号处理的机制,探讨常见问题、易错点及应对策略,并通过代码示例加深理解。
Jimaks
2024/04/26
1.1K0
Golang 语言中怎么拦截系统信号和优雅退出 http server?
系统信号是在类 Unix 系统中用来进程间通讯的一种方式。我们可以使用 kill -l 命令查看各个系统支持的信号列表,每个信号都有名称和编号。我们可以使用 kill 命令给特定进程发送指定信号名称或信号编号的系统信号。
frank.
2021/06/22
1.7K0
从别人的代码中学习golang系列--01
自己最近在思考一个问题,如何让自己的代码质量逐渐提高,于是想到整理这个系列,通过阅读别人的代码,从别人的代码中学习,来逐渐提高自己的代码质量。本篇是这个系列的第一篇,我也不知道自己会写多少篇,但是希望自己能坚持下去。
coders
2020/07/03
8330
Golang语言中的系统Signal处理
我们在生产环境下运行的系统要求优雅退出,即程序接收退出通知后,会有机会先执行一段清理代码,将收尾工作做完后再真正退出。我们采用系统Signal来 通知系统退出,即kill pragram-pid。我们在程序中针对一些系统信号设置了处理函数,当收到信号后,会执行相关清理程序或通知各个子进程做自清理。kill -9强制杀掉程序是不能被接受的,那样会导致某些处理过程被强制中断,留下无法恢复的现场,导致消息被破坏,影响下次系统启动运行。 最近用Golang实现的一个代理程序也需要优雅退出,因此我尝试了解了一下Gol
李海彬
2018/03/20
1.5K0
golang的httpserver优雅重启
去年在做golangserver的时候,内部比较头疼的就是在线服务发布的时候,大量用户的请求在发布时候会被重连,在那时候也想了n多的方法,最后还是落在一个github上的项目,facebook的一个golang项目grace,那时候简单研究测试了一下可以就直接在内部使用了起来,这段时间突然想起来,又想仔细研究一下这个项目了。 从原理上来说是这样一个过程:
黑光技术
2019/03/06
1.1K0
[golang]golang signal.Notify 信号,如何优雅的退出
Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。
landv
2020/06/16
18.6K1
go项目如何优雅关机或重启
编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kill -9 pid的方式会强制关闭进程,这样就会导致服务端当前正在处理的请求失败
Michel_Rolle
2023/11/16
2.7K0
平滑重启你的后台TCP服务
后台业务一般都是通过TCP协议提供服务。服务难免需要版本升级,需要经历旧进程的退出和新进程的启动。为保证用户链接不异常中断,需要旧进程继续运行,直至处理完用户请求后再退出。这样才不会打断用户请求,这就是所谓的Graceful Shutdown:优雅退出。如果不做优雅退出,用户交互过程中任何一个步骤可能被升级打断,往小了有些不重要的业务,中断一下可以忍受,但如支付的基础服务,升级服务如果不支持优雅退出,造成大量用户掉线,进而造成恶劣的影响。所以对服务实现,不论对什么业务来说都是很有必要的。这也是为什么Go从1.8版本开始,标准库net/http对HTTPServer就添加了一个新的方法GracefulShutdown,使得进程可以把现有请求都处理完了再退出。
glendai
2022/02/11
2.5K0
一次golang程序无故频繁重启的问题
程序启动时会打印进程号,同时有系统signal信号捕捉程序,会将程序退出的所有能捕捉的信号都捕捉并打印,然后退出。
天地一小儒
2022/12/28
1.1K0
一次golang程序无故频繁重启的问题
Golang信号处理和如何实现进程的优雅退出
各操作系统的信号定义或许有些不同。下面列出了POSIX中定义的信号。 在linux中使用34-64信号用作实时系统中。 命令 man 7 signal 提供了官方的信号介绍。也可以是用kill -l来快速查看 列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。 Linux支持的标准信号有以下一些,一个信号有多个值的是因为不同架构使用的值不一样,比如x86, ia64,ppc, s390, 有3个值的,第一个值是slpha和sparc,中间的值是 ix86, ia64, ppc, s390, arm和sh, 最后一个值是对mips的,连字符-表示这个架构是缺这个信号支持的, 第1列为信号名; 第2列为对应的信号值,需要注意的是,有些信号名对应着3个信号值,这是因为这些信号值与平台相关,将man手册中对3个信号值的说明摘出如下,the first one is usually valid for alpha and sparc, the middle one for i386, ppc and sh, and the last one for mips. 第3列为操作系统收到信号后的动作,Term表明默认动作为终止进程,Ign表明默认动作为忽略该信号,Core表明默认动作为终止进程同时输出core dump,Stop表明默认动作为停止进程。 第4列为对信号作用的注释性说明。
黑光技术
2019/03/06
2.9K0
相关推荐
Go 语言优雅退出:让程序体面“退休”
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档