协程管理
goroutine的管理
-
runtime.GOMAXPROCS(2)
分配2个逻辑处理器给调度器使用 -
runtime.Gosched()
当前goroutine从当前线程退出,并放回到队列 -
runtime.NumGoroutine()
查看当前存在的协程数 -
通过带缓冲的channel可以实现对goroutine数量的控制
-
控制协程数量代码:
package main import ( "fmt" "runtime" "time" ) type GLimit struct { Limit int ch chan struct{} } func NewGLimit(limit int) *GLimit { return &GLimit{ Limit: limit, ch: make(chan struct{}, limit), } } func (glimit *GLimit) Run(foo func()) { glimit.ch <- struct{}{} go func() { foo() <-glimit.ch }() } func add() { time.Sleep(100 * time.Millisecond) _ = 4 } func main() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() go func() { for { <-ticker.C fmt.Printf("go routine number %d\n", runtime.NumGoroutine()) } }() gl := NewGLimit(100) for { gl.Run(add) } }
-
优雅地退出守护协程
- 守护协程:独立于控制终端和用户请求的协程,它一直存在,周期性执行某种任务或等待处理某些发生的事件。伴随着main协程的退出,守护协程也退出。
- kill命令不是杀死进程,它只是向进程发送信号kill -s pid,s的默认值是15.常见的终止信号如下:
信号 | 值 | 说明 |
---|---|---|
SIGINT | 2 | Ctrl+C触发 |
SIGKILL | 9 | 无条件结束程序,不能捕获、阻塞或忽略 |
SIGTERM | 15 | 结束程序,可以捕获、阻塞或忽略 |
监听信号示例代码
package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
)
var ctx context.Context
var cancle context.CancelFunc
func init() {
ctx, cancle = context.WithCancel(context.Background())
}
// 监听一个信号
func listenSignal() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
for {
select {
case sig := <-c:
fmt.Printf("receive signal %d\n", sig)
cancle()
return
}
}
}
// 监听前端请求
func listenHttp(port int) {
server := &http.Server{Addr: ":" + strconv.Itoa(port), Handler: nil}
go func() {
for {
select {
case <-ctx.Done():
server.Close()
return
}
}
}()
server.ListenAndServe()
fmt.Printf("stop listen http request on port %d\n", port)
}
func main() {
go listenHttp(8080)
go listenHttp(8081)
go listenSignal()
time.Sleep(10 * time.Second)
}
协程管理组件
- go get github.com/x-mod/routine
- 封装了常规的业务逻辑:初始化、收尾清理、工作协程、守护协程、监听term信号
- 封装了常见的协程组织形式:并行、串行、定时任务、超时控制、重试、profiling