goroutineによる非同期処理で、
- 100要素の配列を作る
- 配列の中身を1個ずつスライスに追加していく (元の配列と、中身の同じスライスが生成されるはず)
- スライスの要素数と中身を出力する
というプログラムを作ってみる。
最初のコード
var wg sync.WaitGroup
var numbers = [100]int{} // 100要素ある配列
copiedNumbers := []int{} // 空のスライス
for i := range numbers {
wg.Add(1) // 非同期処理のカウンタをインクリメント
go func() { // 非同期処理の記述
copiedNumbers = append(copiedNumbers, i)
wg.Done() // 非同期処理のカウンタをデクリメント
}()
}
wg.Wait() // 非同期処理が終わるのを待つ
fmt.Println(len(copiedNumbers)) // スライスの要素数を出力
fmt.Println(copiedNumbers) // スライスの中身を出力
結果
- スライスの要素数が100個に満たない
- プログラムを走らせるたび、気まぐれに要素数は変動する
- 重複した値も含まれる
86
[4 6 6 12 12 13 19 21 21 28 28 28 28 28 28 28 28 29 34 34 35 35 36 36 37 38 39 40 41 42 43 44 44 45 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 63 63 77 77 77 77 77 77 77 77 77 77 77 77 77 77 89 90 91 91 91 91 91 91 91 91 91 92 93 93 93 93 99 99 99 99 99]
go func に値を渡すようにする
var wg sync.WaitGroup
var numbers = [100]int{}
copiedNumbers := []int{}
for i := range numbers {
wg.Add(1)
- go func() {
+ go func(i int) {
copiedNumbers = append(copiedNumbers, i)
wg.Done()
- }(i)
+ }(i)
}
wg.Wait()
fmt.Println(len(copiedNumbers))
fmt.Println(copiedNumbers)
結果
- スライスの要素数は100個に満たないまま
- 値の重複はなくなった
78
[1 0 2 4 8 6 7 9 10 11 16 12 13 14 15 19 17 99 27 28 29 30 31 32 33 34 35 36 37 69 68 71 72 73 38 74 39 75 40 76 41 77 78 42 79 80 44 82 45 83 46 84 47 85 48 86 49 88 50 89 51 90 91 95 92 93 55 94 56 62 63 60 64 61 65 58 66 67]
Mutexでロックをかける
+ var mutex = &sync.Mutex{}
var wg sync.WaitGroup
var numbers = [100]int{}
copiedNumbers := []int{}
for i := range numbers {
wg.Add(1)
go func(i int) {
+ mutex.Lock()
copiedNumbers = append(copiedNumbers, i)
+ mutex.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(len(copiedNumbers))
fmt.Println(copiedNumbers)
結果
- 要素数がちゃんと100個になった
- 値の重複がない
- 数字の出現順がランダムなので、非同期処理もできてるっぽい
100
[2 0 1 4 3 5 18 25 6 7 8 19 9 10 20 11 21 22 23 24 15 12 13 16 14 17 31 26 27 29 30 28 34 36 35 37 32 33 38 54 39 40 41 42 43 44 45 46 47 48 49 50 51 52 73 53 55 56 64 65 57 66 58 67 59 69 70 71 68 72 60 63 62 61 99 86 79 80 74 87 81 75 88 76 82 89 83 77 90 84 78 91 85 92 95 97 98 93 96 94]
検証用コード
package main
import (
"fmt"
"sync"
)
func main() {
copy_slice_without_pass_argument()
copy_slice_without_lock()
copy_slice_with_lock()
}
func copy_slice_without_pass_argument() {
var wg sync.WaitGroup
var numbers = [100]int{}
copiedNumbers := []int{}
for i := range numbers {
wg.Add(1)
go func() {
copiedNumbers = append(copiedNumbers, i)
wg.Done()
}()
}
wg.Wait()
fmt.Println(len(copiedNumbers))
fmt.Println(copiedNumbers)
}
func copy_slice_without_lock() {
var wg sync.WaitGroup
var numbers = [100]int{}
copiedNumbers := []int{}
for i := range numbers {
wg.Add(1)
go func(i int) {
copiedNumbers = append(copiedNumbers, i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(len(copiedNumbers))
fmt.Println(copiedNumbers)
}
func copy_slice_with_lock() {
var mutex = &sync.Mutex{}
var wg sync.WaitGroup
var numbers = [100]int{}
copiedNumbers := []int{}
for i := range numbers {
wg.Add(1)
go func(i int) {
mutex.Lock()
copiedNumbers = append(copiedNumbers, i)
mutex.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(len(copiedNumbers))
fmt.Println(copiedNumbers)
}
環境
- go version go1.8.1 darwin/amd64
参考
チャットメンバー募集
何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。