Go言語で並行処理をする方法

Go言語で並行処理をする方法

Go言語で並行処理を行うには、主に「ゴルーチン」と「チャネル」を使います。
Goは並行処理のためのサポートが豊富で、これにより複数の処理を効率よく実行できます。

ゴルーチン

ゴルーチンは、Go言語の並行処理の基本単位です。
関数やメソッドをゴルーチンとして実行するには、関数呼び出しの前にキーワード go を付けます。
これにより、指定した関数が新しいゴルーチンとして非同期に実行されます。
ゴルーチンは軽量で、システムのスレッドよりも少ないリソースで並行処理を実現できます。

以下に、ゴルーチンの基本的な使用例を示します。

package main

import (
  "fmt"
  "time"
)

func sayHello() {
  for i := 0; i < 5; i++ {
    fmt.Println("Hello")
    time.Sleep(1 * time.Second)
  }
}

func sayGoodbye() {
  for i := 0; i < 5; i++ {
    fmt.Println("Goodbye")
    time.Sleep(1 * time.Second)
  }
}

func main() {
  go sayHello() // ゴルーチンとして実行
  go sayGoodbye() // ゴルーチンとして実行

  // メインゴルーチンが終了する前に、サブゴルーチンの処理が終了するのを待つ
  time.Sleep(6 * time.Second)
}

このコードでは、sayHello と sayGoodbye の2つの関数がゴルーチンとして並行に実行されます。
time.Sleep を使用してメインゴルーチンがサブゴルーチンの終了を待っていますが、実際のアプリケーションでは sync.WaitGroup などを使用してゴルーチンの完了を待機することが推奨されます。

チャネル

チャネルは、ゴルーチン間でデータをやり取りするための通信手段です。
チャネルを使うことで、ゴルーチン間の同期やデータ共有を簡単に行うことができます。
チャネルの基本的な操作には、送信(<- 演算子を使う)と受信(<- 演算子を使う)が含まれます。

以下に、チャネルを使った例を示します。

package main

import (
  "fmt"
  "time"
)

func generateNumbers(ch chan int) {
  for i := 1; i <= 5; i++ {
    ch <- i // チャネルに値を送信
    time.Sleep(1 * time.Second)
  }
  close(ch) // チャネルを閉じる
}

func main() {
  ch := make(chan int) // チャネルの作成

  go generateNumbers(ch) // ゴルーチンとして実行

  for num := range ch { // チャネルから値を受信
    fmt.Println(num)
  }
}

この例では、generateNumbers 関数が整数をチャネルに送信し、メインゴルーチンがその整数を受信して表示します。
チャネルが閉じられると、range ループは自動的に終了します。

同期

ゴルーチンが完了するのを待機するためには、sync.WaitGroup を使うことが一般的です。
以下にその例を示します。

package main

import (
  "fmt"
  "sync"
)

func printNumbers(wg *sync.WaitGroup) {
  defer wg.Done()
  for i := 1; i <= 5; i++ {
    fmt.Println(i)
  }
}

func main() {
  var wg sync.WaitGroup
  wg.Add(1) // ゴルーチンの数を追加

  go printNumbers(&wg) // ゴルーチンとして実行

  wg.Wait() // ゴルーチンが完了するのを待機
}

sync.WaitGroup を使用することで、メインゴルーチンがサブゴルーチンの完了を待機し、正確に同期処理を行うことができます。

このように、Go言語ではゴルーチンとチャネルを駆使することで、並行処理を簡潔かつ効率的に実現できます。
並行処理を効果的に活用することで、パフォーマンスの向上やレスポンシブなアプリケーションの構築が可能になります。