普段仕事ではPHPを使って開発しているのですが、興味本位でGoを触り始めました。
はい、完全な趣味です。
「仕事しろ!」って怒られそうです。
そんな中、PHPではあまり聞かない「並行処理」について
「これ便利やな〜」
と感じたため、メモ書きしておきます。
並行処理とは
プログラムは、コンピュータに対して「何かをする」指示を与えるものです。
通常、プログラムは順番に命令を実行していきます。
しかし並行処理では、複数の命令を同時に実行することができます。
Goでは、このような並行処理を簡単に行うことができる仕組みが提供されています。
ゴールーチン(Goroutine) と呼ばれる軽量なスレッドを使用することができるのです。
ゴールーチンとチャネル
ゴールーチンは、一つのプログラム内で複数作成することができます。
それぞれのゴールーチンは、異なる処理を同時に実行することができます。
ゴールーチン同士は、情報をやり取りするために チャネル(Channel) という仕組みを使います。
チャネルは、ゴールーチン間でデータを送受信するためのパイプのようなものです。
例えば、友達が机で勉強しているゴールーチンと、あなたが宿題をしているゴールーチンがあるとします。
チャネルとは、友達とあなたがメモを渡し合ってコミュニケーションを取るイメージです。
ゴールーチンとチャネルを使うことで、プログラム内で複数のタスクを同時に処理することができます。
友達が勉強している間に、あなたは別の作業を並行して行うことができるわけです。
Goは並行処理がお得意
Goの並行処理は、前述したゴールーチンとチャネルによってプログラムをより効率的に実行されます。
複数のタスクを同時に処理することで、プログラムの実行時間を短縮したり、処理能力を最大限に活用したりすることが期待できます。
こちらの記事で処理速度についてわかりやすく説明してくださっています。
実装例の紹介
package main
import (
"fmt"
"time"
)
func goroutineA() {
for i := 1; i <= 5; i++ {
fmt.Println("ゴールーチンA:", i)
time.Sleep(1 * time.Second)
}
}
func goroutineB() {
for i := 1; i <= 5; i++ {
fmt.Println("ゴールーチンB:", i)
time.Sleep(1 * time.Second)
}
}
func main() {
go goroutineA()
go goroutineB()
time.Sleep(6 * time.Second)
fmt.Println("メイン関数終了")
}
このコードでは、goroutineA()とgoroutineB()という2つのゴールーチンを定義しています。
goキーワードを使用して、それぞれの関数をゴールーチンとして実行します。
両方のゴルーチンは同時に実行され、メイン関数はゴルーチンの終了を待つために一定時間待機します。
この実装例から感じられるメリットとして
- 並行処理がシンプル
- ゴールーチンは非常に軽量であり、数千ものゴールーチンを同時に実行することができちゃう
- ゴールーチン間でのデータの同期や通信は、チャネルを使って簡単に実現できちゃう
などが読み取れます。
並行処理の注意点
並行処理は正しく扱わないと予期せぬ結果や問題が生じる可能性があります。
例えば、複数のゴールーチンが同じ変数に同時にアクセスすると、競合状態(競合条件)が発生し、プログラムの実行結果がおかしくなることがあります。
複数のゴールーチンが同じ変数に同時にアクセスすると、競合状態(競合条件)が発生し、プログラムの実行結果がおかしくなることがあります。
競合状態を避けるために、適切な同期機構を使ってゴールーチンの実行順序やデータの整合性を保つ必要があります。
その対策としてGoでは、ミューテックス(Mutex) や セマフォ(Semaphore) などの同期機構を提供しています。これらを使うことで、ゴールーチンの実行を制御し、競合状態を回避することができます。
今後のGoに注目
PHPより案件数が少ないGoですが、優れた面があることがわかったと思います。
Gohpher人口の増加も夢ではない?!
ちなみにですが、PHPでも並行処理に似たようなことができるようです。
それはまたの機会に・・・。