https://qiita.com/castaneai/items/7815f3563b256ae9b18d
Goにはgoroutineという特定の関数を非同期で実行させられる便利な機能があります。 しかし、名前通り非同期処理なので実装によっては「裏でずっと無駄に動いていた」「気づいたら裏で増殖していた」といった事態になります。
Goに限らずあらゆるプログラムで言えることですが、 一度起動した非同期処理は終了までちゃんと面倒を見る ことが重要です。 もう少し具体的な例でいうと非同期処理はキャンセル(中断)を考慮しよう、というものが挙げられます。 本記事ではGoの非同期処理システムであるgoroutineを止める方法を解説していきます。
いきなり結論からですが、goroutineを止める専用のAPIは存在しません。
では、どうすればgoroutineを止められるのでしょうか? 答えは簡単で goroutineで起動した関数をreturnして終わらせるだけです。 「いや、そんなことはわかってるよ!」って感じですね。 そういうことではなく、多くの人が知りたいのは 裏で動いているgoroutineをメインのスレッドからいい感じに中断させるにはどうすればいいか? だと思います。
たとえば、次のように無限ループで何かを処理し続けるgoroutineがあるとします。 これをメイン処理から止めたい場合どうすればいいでしょうか?
package main
import (
"time"
)
func main() {
go loop()
// 何かの処理
// ここで loop() を止めたい
time.Sleep(3 * time.Second)
println("finish")
}
// 無限ループする関数
func loop() {
for {
println("loop...")
time.Sleep(1 * time.Second)
}
}
別のgoroutineに何かを通知したい場合、定番なのは channel を使う方法です。 流れとしては次のようになります。
return
で関数を終わらせる(止まった)package main
import (
"time"
)
func main() {
cancel := make(chan struct{})
go loop(cancel)
// 何かの処理
// ここで loop() を止める
close(cancel)
time.Sleep(3 * time.Second)
println("finish")
}
// 無限ループする関数
func loop(cancel chan struct{}) {
for {
select {
case <-cancel:
// 中断通知が来た
println("cancel")
return
default:
println("loop...")
time.Sleep(1 * time.Second)
}
}
}
この実装にはいくつかポイントがあります。
struct{}
でよいclose()
でよい