LoginSignup
11
10

More than 5 years have passed since last update.

golangでシグナルを拾ってgracefulにgoroutineを停めたい・改

Last updated at Posted at 2015-11-10

golangでシグナルを拾ってgracefulにgoroutineを停めたい・改

これの続き。
http://qiita.com/arc279/items/c44d4a18a851ff454c64

ちょっとまとめてみた。名前は適当。
使い方は main() を見られたし。

main.go
package main

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

type Gracefully struct {
    trapSig  []os.Signal
    callback func(os.Signal) bool
    procs    []func()
}

func NewGracefully(callback func(os.Signal) bool, sig ...os.Signal) *Gracefully {
    self := Gracefully{
        trapSig:  sig,
        callback: callback,
        procs:    make([]func(), 0),
    }
    return &self
}

func (self *Gracefully) Append(proc func()) {
    self.procs = append(self.procs, proc)
}

func (self *Gracefully) Run() {
    var wg sync.WaitGroup
    wg.Add(len(self.procs))

    exitChan := make([]chan int, len(self.procs))
    for i, _ := range self.procs {
        exitChan[i] = make(chan int, 1)
    }

    for i, proc := range self.procs {
        go func(ch chan int, proc func()) {
            defer wg.Done()
            for {
                select {
                case <-ch:
                    return
                default:
                    // なんらかの並行処理するやつ
                    proc()
                }
            }
        }(exitChan[i], proc)
    }

    signalChan := make(chan os.Signal)
    signal.Notify(signalChan, self.trapSig...)

    go func() {
        for {
            sig := <-signalChan
            for _, trap := range self.trapSig {
                if sig == trap {
                    if self.callback(sig) {
                        // callbackがtrueを返したら抜ける
                        for _, c := range exitChan {
                            c <- 1
                        }
                    }
                }
            }
        }
    }()

    wg.Wait()
}

func main() {

    // HUP と USR2 を拾って、HUPの時は抜けたいコールバック
    g := NewGracefully(func(sig os.Signal) bool {
        fmt.Println(sig)
        return sig == syscall.SIGHUP
    }, syscall.SIGHUP, syscall.SIGUSR2)

    procs := []string{
        "proc1",
        "proc2",
        "proc3",
        "proc4",
        "proc5",
        "proc6",
        "proc7",
    }

    for _, p := range procs {
        // javascriptみたいw
        g.Append(func(s string) func() {
            return func() {
                fmt.Println(s)
                time.Sleep(500 * time.Millisecond)
            }
        }(p))
    }

    g.Run()

    fmt.Println("stop gracefully")
    os.Exit(0)
}

ビルドして実行

$ go build -o sample main.go && ./sample

シグナルを送る

HUPだと停まる

$ kill -SIGHUP `pgrep -f sample`

USR2だと停まらない

$ kill -SIGUSR2 `pgrep -f sample`

それ以外は拾わない

$ kill -SIGUSR1 `pgrep -f sample`
11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10