go语言使用通道实现协程优雅退出策略

在编写代码过程中如果使用了协程,通常在主程序中使用wg.Wait()等待所有协程处理结束后才能执行主程序的退出
例如

package main

import (
	"fmt"
	"sync"
	"time"
)
var wg1 sync.WaitGroup
func f1(){
	time.Sleep(time.Second*5)
	fmt.Println("aaaaa")
	wg1.Done()
}


func main(){
	wg1.Add(1)
	go f1()
	wg1.Wait()
	fmt.Println("quit success")
}

当fi等待5s后执行wg1.Done(),此时主函数中wg1.Wait()进入非阻塞,函数退出。
go语言使用通道实现协程优雅退出策略_第1张图片
但是,当主程序非正常退出时,fi协程就不会优雅的退出
在这里插入图片描述
如何优雅的退出?

package main

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


var wg sync.WaitGroup
var in=make(chan int,1)
var quit=make(chan int,1)
var quito0k=make(chan int,1)

func ListenQuit(){
	defer wg.Done()
	var sign=make(chan os.Signal,1)
	signal.Notify(sign,syscall.SIGINT)
	<-sign
	SQuit()

}

func run(){
	defer wg.Done()
Quit:
	for {
		select {
		case out := <-in:
			fmt.Println(out)
		case <-quit:
			break Quit
		}
	}

}

func producerin(){
Quit:
	for {
		select {
		case in<-0:
		case <-quit:
			break Quit
		}
	}

}

func SQuit(){
	close(quit)
	go func(){
		wg.Wait()
		quito0k<-0
	}()
}
func main(){
	go ListenQuit()
	go func(){
		for i := 0; i <10; i++ {
			wg.Add(1)
			go run()
		}
	}()
	wg.Add(1)
	go producerin()
	<-quito0k
	fmt.Println("qiut ok")

}

首先在最开始创建一个监听协程go ListenQuit(),当没有退出信号时,该协程会一直阻塞,

signal.Notify(sign,syscall.SIGINT)
	<-sign

创建一个生产协程和n个消费协程

	go func(){
		for i := 0; i <10; i++ {
			wg.Add(1)
			go run()
		}
		...
		go producerin()

两个协程会一直循环生产处理in通道中的消息
当监听退出的协程,监听到信号后,会进入非阻塞状态,执行SQuit()、close(quit),从而使producerin和run协程中的循环停止(随机选取case总会处理case <-quit:)

func run(){
	defer wg.Done()
Quit:
	for {
		select {
		case out := <-in:
			fmt.Println(out)
		case <-quit:
			break Quit
		}
	}

}

参照:
https://blog.csdn.net/qq_34673519/article/details/100109640
https://blog.csdn.net/qq_34673519/article/details/100110194

	go func(){
		wg.Wait()
		quito0k<-0
	}()
	...
	<-quito0k

当所有协程均正常退出后,main进入非阻塞,退出。

你可能感兴趣的:(go语言学习)