how to stop groutine with channel


overview

バックグラウンド等で動かすgoroutineを使ったforループプログラムで、シグナルを受け取った際に終了させる処理例

example1

ここではシグナルの代わりに適当なチャンネルを使う。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan string, 1)
    sig := make(chan string, 1)
    go func(){
        for{
            fmt.Println("Hello")

            select{
            case s := <-c:
                fmt.Println(s)
                sig<-"FINISH"
                return
            default:
            }
            time.Sleep(1 * time.Second) //
        }
    }()

    time.Sleep(10 * time.Second)
    c<-"done"
    s:= <-sig
    fmt.Println(s)
}

example2

実際にシグナルを使った例

package main

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

func Loop(c chan os.Signal, done chan int) {
    go func() {
        for {
            fmt.Println("do something....")
            select {
            case s := <-c:
                fmt.Println("Got signal:", s)
                close(c)
                done <- 0
                return
            default:
            }
            time.Sleep(1 * time.Second)
        }
    }()
}
func main() {
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)

    done := make(chan int)
    defer close(done)

    Loop(sig, done)

    <-done
    fmt.Println("Exit")
    os.Exit(0)
}

実行結果

  • mainではdoneチャンネル受信待ちにする。
  • シグナルを送るとチャンネルに送信される。
  • 所定の処理が終わったあとにチャネルを受信する。シグナルが来ていなければ処理を継続。シグナルが来ている場合はdoneチャンネルに送信する、mainでdoneチャンネルを受信し処理を終了する。
$ go run signal.go
do something....
do something....
do something....
do something....
do something....
do something....
do something....
do something....
do something....
do something....
do something....
^Cdo something....
Got signal: interrupt
Exit