DBへ接続してデータを取得したり、Web Apiを実行してデータを取得したりと様々なデータソースからデータを取得する事がよくあります。
DBからのデータ取得、WebApiでのデータ取得が独立している場合、一方のデータ取得を待たずに各々からそれそれデータが取得出来ると必要なデータの取得時間が短縮できます。Goではそんな時に並行処理を使います。
Goの並行処理の基本では、ゴルーチン、チャネルが主な登場人物になります。特にチャネルの使い方が重要になります。
ゴルーチン
ゴルーチンとは
Goで並行処理を実現する場合、ゴルーチンを使います。ゴルーチンはGoのルーチンで、このルーチンはGoが取り扱う軽微なスレッドにより実行されます。イメージしにくいので実際に使用してみた方がイメージしやすいです。
使い方
実行する関数の前に go をつけて実行します。こうする事で関数がサブルーチン(ゴルーチン)として実行されます。呼び出し元とは別のスレッドにより関数が実行されるので、呼び出し元と呼び出し先(ゴルーチン)は連動しません。非同期です。
go sampleFunc()
使用例
例として、処理に5秒かかる関数を使用します。メインルーチン(いつものmain関数)から、この5秒かかる関数をゴルーチンで実行してみます。
メインルーチンとは別に5秒かかる関数がサブルーチンで実行されるので、メインルーチンが先に終了します。従って関数sampleGoRoutineA()の”【終了】”は表示されません。サブルーチンがメインルーチンとは同期(連動?)していない事がわかります。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA() {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
}
func main() {
go sampleGoRoutineA()
}
チャネル
メインルーチンが先に終了しては意味がありません。サブルーチンの結果を取得したいですね。その場合、チャネルというものを利用できます。
チャネルとは
ルーチン(関数)同士で値の送受信が行えるものです。
使い方
チャネル変数
makeを使って作成します。キーワードchanに使用する型を定義します。
channelA := make(chan string)
makeで作成しているので参照型(ポインタ)となります。
書き込み、読み込み
ルーチン(関数)同士がチャネルを通じて値の送受信を行う為、チャネルに対して書き込み、読み込みが行えます。
以下は書き込みの例です。
channel <- "sampleGoRoutineA"
読み込みの例
val := <-channel
書き込み、読み込み専用
チャネルを関数へ渡す場合、書き込み専用か、読み込み専用かを宣言します。
書き込み、読み込み専用として宣言しなくても動作しますが、宣言しておくことでGoがコンパイル時読み込み専用に書き込みを行なっていないか、またはその逆をしていないかを検知してくれます。
// 書き込み専用
func sampleGoRoutineA(channel chan<- string) {
channel <- "sampleGoRoutineA"
}
// 読み込み専用
func sampleGoRoutineB(channel <-chan string) {
val := <-channel
}
使用例
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
channel <- "終了 \n"
}
func main() {
channelA := make(chan string)
go sampleGoRoutineA(channelA)
val := <-channelA
fmt.Println(val)
}
結果は以下のようになります。
【開始】 18:54:23 sampleGoRoutineA
【終了】 18:54:28 sampleGoRoutineA
終了
チャネルの説明
今回はメインルーチン(main関数)とゴルーチン(sampleGoRoutineA関数)がチャネルを通じて値の送受信を行いました。
サブルーチンは5秒してからチャネルへ値の書き込みを行います。その間メインルーチンではゴルーチンより先にチャネルを読み込もうとして処理をブロック(待機)します。
val := <-channelA
5秒後、チャネルに値が書き込まれたらブロックを解除して処理が進みます。
最後に
今回はGoの並行処理に必要な要素ゴルーチンとチャネルについて説明し、初歩的な使い方を説明しました。