Go言語 ~基礎編~ GoroutineとChannel

go

今回は、GoroutineとChannelを学びましょう。

Goroutineは、複数の異なるtaskを一度に実行することができる、いわゆる”並列処理“を可能にするものです。

Channelは、Goroutineによる並列処理において、データのやりとりを行う際に使用されます。

学習記事まとめ

Goroutineによる並行処理

プログラムの実行速度を向上させたい場合の取りうる選択肢の一つは、「処理のマルチタスク化」です。

一度に一つのtaskをこなすより、複数のtaskを同時にこなした方が処理時間を短くすることが可能です。

例えば、次のプログラムを例にあげてみましょう。

このプログラムは、main関数に設定されたURLへコネクションを貼り、そのページのサイズを取得します。

実行してみましょう。

上記の実行結果の通り、通常は読み込まれた関数順に逐次処理でプログラムが実行されます。

そのため、処理時間がそれなりにかかってしまいますね。

これをGoroutineを使って解決します。

Goroutineを使うには、関数やメソッドの前に”go“キーワードをつけるだけです。

簡単なサンプルを以下に記述します。

このサンプルコードは、並列処理でないバージョンのものです。

このプログラムを実行すると以下の通りになります。

one関数から順次実行されているのがわかると思います。

それでは、goキーワードを付けたバージョンをみていきましょう。

今度は、one関数およびtwo関数の呼出しにgoキーワードを付与しています。

プログラムを実行してみましょう。

しかし、出力された結果を見ると、one関数や two関数の処理結果が表示されていません。

実は、Goroutineが未完了の状態でも、プログラムの最後の関数が実行された時にプログラムは終了してしまいます。

この事象の回避策の一つは、timeパッケージのSleep関数を使うことです。

main関数に設定しました。

プログラムを実行してみましょう。

先ほどとは違って並列実行のため、処理が幾分早く終了します。

goキーワードを使って、先ほどのgetWebPageSize関数の呼び出しをGoroutine化してみましょう。

終了時間的にはGoroutineの方が遅く感じられますが、それはtime.Sleep関数を実行しているからです。ページ容量については、このプログラムの方が早く表示されます(実際に実行するとわかります)。

goステートメントは戻り値を返せない件

大変便利そうなGoroutineですが、一つ問題があります。

それは、”戻り値を返せないこと“です。

先ほどのソースコードを書き換えて、確認してみましょう。

getWebPageSize関数をページサイズを返却できるように書き換えました。

このプログラムを実行してみます。

ご覧の通り、エラーが発生しましたね。

Goroutineの実行はいつ終了するのか保証されていません。そのため、戻り値を取得して利用することはできないようです。

このような場合、Go言語では”channel“を使って、データのやりとりを行います。

channelを使うには、いくつかの決まりごとがあります。

具体例を以下に記述します。

この例では、main関数でchannelを作成しています。その後、Goroutineにてhello関数を実行し、myChannelへ文字列”Hello”を送信しています。

そして、hello関数の呼び出し元でmyChannelから”Hello”を受け取り、terminalへ出力しています。

プログラムを実行してみましょう。

channelによるデータの送信は、受診側がそのデータの使用を試みる前に必ず送信されます。

channelは、gorutineの中でこのデータを”ブロッキング“しているのです。

これにより、データを取り出すタイミングが自由自在です。

やってみよう

getWebPageSize関数を実装したプログラムでは、戻り値の返却に失敗していました。

これをchannelを使ったプログラムに書き換えて、きちんと動作するように修正しましょう。

サンプルコードを以下に記述しておきます。

問題なく処理できましたね。

しかし、出力結果には処理したURLとページサイズが出力されていますが、このままだとどれがどれのページサイズなのかわかりません。

URLとページサイズの結びつきがわかるように、コードをバージョンアップしましょう。

Structを使って、データをまとめました。だいぶ分かりやすくなったと思います。

まとめ

KOUKI
KOUKI

最後にこの章で学んだことをまとめます。

gorutineとchannel まとめ
・ 処理のマルチタスク化をしたい場合、Goroutineを使用する
・ Goroutineの宣言には、goキーワードを関数またはメソッドの直前に使用する
・ Goroutineが未完了の場合でもプログラムが終了したら一緒に終了する
・ Goroutineの処理を待ちたい場合は、time.Sleep関数を使用する
・ goキーワードを使った関数では、戻り値を設定できない
・ Goroutine間のデータのやりとりには、channelを用いる
・ channelは、chanキーワードと共にmake関数で作成する
・ channelの送受信には、<-オペレーターを用いる

次回

次回は、Go言語でのテストコードの書き方を学びましょう。

コメントを残す