Go main終了によるgoroutine終了の抑止

2022/03/13に公開

前提

メインゴルーチンが終了するとプログラムは終了するので、goroutineを切った場合の処理も中断される
それを止めない方法の紹介
あるコンテナを常駐させるときなどに便利
主にchannel待ちを使う

以下のプログラムは(goroutineの実行状態を無視して)即終了する

func main() {
	go func() {
		for {
			// 処理
		}
	}()
}

select {}

アドホックな方法1

package main

func main() {
	go func() {
		for {
			// 処理
		}
	}()
    select {}
}

selectはチャネルの受信を待って区別して分岐、だが終了条件を定義していなければずっと待ちに入り、mainは終了しない

受信待ち

アドホックな方法2

package main

func main(){
	go func() {
		for {
			// 処理
		}
	}()
    ch := make(chan struct{})
    <-ch
}

chの受信を待つがchに対し送信をしていないため、ずっと受信を待ち続けmainは終了しない

sync.WaitGroup.Done()しない

使い道違うだろうけど、一応

package main

import "sync"

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		for {
            // 処理
		}
	}()

	wg.Wait()
}

Done()しないとWait()したままなので、mainは終了しない

この例はむしろDone()漏れしたので終了すべきプログラムが終了しない、というコーディング上のポカとしてありそう

ちなみに

いずれの例も、goroutineを切らずに停止しない処理だけ書いて実行すると、デッドロックになる

package main

func main() {
	select {}
}
$ go run .
fatal error: all goroutines are asleep - deadlock!

Discussion