package main
import (
"fmt"
"math/rand"
"time"
)
func processChannel(ch chan int) []int {
const maxConc = 10
results := make(chan int, maxConc)
for i := 0; i < maxConc; i++ {
go func() {
v := <- ch
results <- process(v)
}()
}
fmt.Println("ゴルーチン 起動完了")
var out []int // intのスライス
for i:=0; i<maxConc; i++ {
out = append(out, <-results) // 結果を受け取って後ろに追加
}
return out
}
func process(v int) int {
returnVal := v*v
rand.Seed(time.Now().UnixNano()) // シードの指定
sleepSec := rand.Intn(3) // 0以上3未満の整数を戻す
fmt.Println("process:", v, returnVal, sleepSec)
time.Sleep(time.Duration(sleepSec)*time.Second)
return returnVal
}
func main() {
ch := make(chan int)
var result []int;
go func() { // 処理してもらう数値をchに入れる
for i := 0; i < 100; i++ {
ch <- i
}
}()
result = processChannel(ch)
fmt.Printf("result: %d\n", result)
}
func processChannel(ch chan int) []int {
processChannel
という関数を定義しています。この関数は、整数型のチャネルch
を受け取り、整数のスライスを返します。
const maxConc = 10
maxConc
という定数を定義し、同時に起動するゴルーチンの数を10に設定しています。
results := make(chan int, maxConc)
results
というチャネルを作成しています。このチャネルは、整数を受け取るためのもので、バッファサイズはmaxConc
(10)です。
for i := 0; i < maxConc; i++ {
go func() {
maxConc
の数だけループを回し、各ループで新しいゴルーチンを開始します。
v := <- ch
チャネルch
から整数を受け取り、それを変数v
に格納します。
results <- process(v)
受け取った整数v
をprocess
関数に渡し、その結果をresults
チャネルに送信します。
}()
}
ここで、ゴルーチンの定義が終了します。これにより、各ループのたびに独立した処理が行われます。
fmt.Println("ゴルーチン 起動完了")
すべてのゴルーチンが起動したことを示すメッセージを出力します。
var out []int // intのスライス
整数のスライスout
を初期化します。結果を格納するために使用します。
for i:=0; i<maxConc; i++ {
out = append(out, <-results) // 結果を受け取って後ろに追加
}
maxConc
回ループを回し、results
チャネルから結果を受け取ってout
スライスに追加します。
return out
}
最終的な結果のスライスout
を返します。
func process(v int) int {
process
という関数を定義しています。この関数は整数v
を受け取り、整数を返します。
returnVal := v*v
引数v
の二乗を計算し、returnVal
に格納します。
rand.Seed(time.Now().UnixNano()) // シードの指定
乱数生成器にシードを設定します。これにより、毎回異なる乱数を生成することができます。
sleepSec := rand.Intn(3) // 0以上3未満の整数を戻す
0以上3未満のランダムな整数を生成し、sleepSec
に格納します。
fmt.Println("process:", v, returnVal, sleepSec)
process
関数の入力値、計算結果、スリープ秒数を出力します。
time.Sleep(time.Duration(sleepSec)*time.Second)
生成した乱数の秒数だけスリープします。これにより、処理がランダムに遅延します。
return returnVal
}
計算結果returnVal
を返します。
ch := make(chan int)
整数型のチャネルch
を作成します。これにより、整数をゴルーチンに送信できます。
var result []int;
結果を格納するための整数スライスresult
を宣言します。
go func() { // 処理してもらう数値をchに入れる
ゴルーチンを起動し、内部で処理する整数をチャネルに送信します。
for i := 0; i < 100; i++ {
ch <- i
}
0から99までの整数をch
チャネルに送信します。
}()
ゴルーチンの定義が終了します。
result = processChannel(ch)
processChannel
関数を呼び出し、チャネルch
からの結果をresult
に格納します。
fmt.Printf("result: %d\n", result)
このコードにおけるバッファ付きチャネルの使用について詳しく解説します。
バッファ付きチャネルとは
Go言語におけるチャネルは、ゴルーチン間でデータを送受信するための構造です。チャネルには「バッファ付き」と「バッファなし」の2種類があります。
- バッファなしのチャネル: 送信と受信が同期的に行われます。つまり、送信側がデータを送ると、受信側がそれを受け取るまで次の処理に進むことができません。
- バッファ付きのチャネル: あらかじめ指定したサイズのバッファを持ち、送信側は受信側が受け取る前に一定数のデータを送信できます。これにより、送信側と受信側の処理が非同期に行われることが可能となります。
バッファ付きチャネル
results := make(chan int, maxConc)
1. バッファサイズの指定
maxConc
(10)をバッファサイズとして指定しています。これにより、最大で10個の整数をチャネルresults
に送信できるようになります。これは、同時に最大10個のゴルーチンが実行され、それぞれがresults
チャネルに結果を送信することを意味します。
2. 非同期処理の実現
バッファ付きチャネルを使用することで、processChannel
関数内のゴルーチンは、他のゴルーチンが結果を受信するのを待たずに、即座にresults
チャネルに結果を送信できます。これにより、各ゴルーチンは独立して動作し、並行処理の利点を最大限に活かすことができます。
3. 結果の収集
processChannel
関数内では、次のようにループを使って結果を収集しています。
for i := 0; i < maxConc; i++ {
out = append(out, <-results)
}
ここでは、results
チャネルから最大10個の結果を受信し、スライスout
に追加しています。バッファ付きチャネルがあることで、受信側が結果を取得する際に、送信側がすでに結果を送信している状態が保証されます。