グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



goroutineリークを排除して安全に並行処理を行う方法

goroutineはgoキーワードを関数の前に書くことで簡単に起動することができます。
しかしながら、goroutineはランタイムによってガベージコレクションされないため、正常に終了させていない場合はリークしていきます。 野放しになったgoroutineたちによってプロセスごと停止にならないよう、正常に終了させましょう。

goroutineリークしている例

それでは、意図的にgoroutineリークを発生させてみましょう。
起動しているgoroutineの数をカウントするため、runtime.NumGoroutine()を使用して現在のgoroutine数を標準出力に表示させています。

出力結果は以下のようになりました。

この処理では leak 関数の引数に nil チャネルを渡しているため、子goroutineの処理が永遠に終わらず、goroutine数が2のまま終了しています。
管理不能なgoroutineが解き放たれてしまいました。
この例ではプロセスがすぐに終了しますが、webアプリケーションなど長期間起動するプログラムではメモリが永遠に消費されてしまいます。

チャネルを使用してタイムアウトさせる

goroutineを適切に処理する方法の1つとして、チャネルを使用してgoroutineをキャンセルさせてみます。
 done チャネルを作成して、1秒経過後にタイムアウトさせます。

出力結果は以下のようになりました。

 done! を出力し、最後のgoroutine数も1になっていることから適切にgoroutineをキャンセルできていることが確認できました。
引数に nil を渡していますが、今回はgoroutineが無事に終了しました。

contextパッケージを使用してタイムアウトさせる

先ほどの例では done チャネルを作成しましたが、contextパッケージを使用するとより単純に記述することができます。
それでは、contextパッケージを使用してタイムアウトさせてみます。

出力結果は以下のようになりました。

contextのエラーを出力し、最後のgoroutine数も1になっていることから、チャネル同様goroutineをキャンセルできていることが確認できました。
contextパッケージにはまだまだ便利な機能が備わっているので、活用していきましょう。

まとめ

goroutineは軽量なスレッドとはいえ、リークしてしまうとプロセスが終了するまで永遠にメモリを使用し続けます。
今回は使用していませんが、検証用にuber-go/goleak のようなパッケージをテストに組み込むのもよさそうです。
goroutineは簡単に使用できますが、安易に使用しないように気をつけていきましょう。

The following two tabs change content below.

totsuka

事業開発部 web application engineer
鉄道会社でバス・不動産部門を経験の後、エンジニア未経験ながらもアーティスへ入社。 事業開発部のエンジニアとして新規サービスの開発に携わっている。 エンジニア未経験での入社のため、つまずきながらも1,2ヶ月の研修を経て、現在では主にGoを使用したプロダクトコードのコーディングに従事している。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします