Goroutine エクササイズ

先輩からお題が出されたので、忘れないように記載しておく

## お題

Sleep関数を2秒以内に50回実施して、停止した最大、最小、平均ミリ秒数を表示してください

``` go
package main

import (
    "math/rand"
    "time"
    "fmt"
)

 func Sleep(i chan int) {
    r := rand.Intn(500) + 100 // 止まるミリ秒数の定義.ランダム数
    time.Sleep(time.Duration(r) * time.Millisecond) // 止まるミリ秒数の定義
    fmt.Printf("I Slept %d millseconds. \n", r)
    i <- r
}

func main() {
    rand.Seed(time.Now().UnixNano())
    // Sleep関数を2秒以内に50回実施して、停止した最大、最小、平均ミリ秒数を表示してください
}
```

## 自分の回答

``` go
package main

import (
	"math/rand"
	"time"
	"fmt"
	"sync"
	"strconv"
)

func Sleep(i chan int) {
	r := rand.Intn(500) + 100 // 止まるミリ秒数の定義.ランダム数
	time.Sleep(time.Duration(r) * time.Millisecond) // 止まるミリ秒数の定義
	fmt.Printf("I Slept %d millseconds. \n", r)
	i <- r
}

func max(a []int) int {
	max := a[0]
	for _, i := range a {
		if i > max {
			max = i
		}
	}
	return max
}

func min(a []int) int {
	min := a[0]
	for _, i := range a {
		if i < min {
			min = i
		}
	}
	return min
}

func avg(a []int) int {
	len := len(a)
	sum := 0
	for _, i := range a {
		sum += i
	}
	return sum / len
}

func main() {
	rand.Seed(time.Now().UnixNano())
	// Sleep関数を2秒以内に50回実施して、停止した最大、最小、平均ミリ秒数を表示してください

	// チャネルを用意
	ch := make(chan int)
	finished := make(chan int)

	// Sleep関数を50回、並列処理で実行
	wg := &sync.WaitGroup{}
	for i := 0 ; i < 50 ; i++ {
		wg.Add(1)
		go func() {
			Sleep(ch)
			wg.Done()
		}()
	}

	// 全ての並行処理が完了するのを待つ、完了次第、本スレッドに通知
	go func(){
		wg.Wait()
		finished <- 1
	}()

	// 本スレッドで、並列で実行した結果を受け取る
	results := []int{}
	LOOP:
		for {
			select {
			case v := <- ch:
				results = append(results, v)
			case <- finished:
				break LOOP
			}
		}

	// 結果の集計
	fmt.Println("件数：" + strconv.Itoa(len(results)))
	fmt.Println("最大：" + strconv.Itoa(max(results)))
	fmt.Println("最小：" + strconv.Itoa(min(results)))
	fmt.Println("平均：" + strconv.Itoa(avg(results)))
}
```

### 実行結果

```
件数：50
最大：573
最小：108
平均：311
```

「ここはこうした方が良い」など、ありましたらコメントいただければ幸いです。


## 別の方法も考えてみた

チャネルに結果を溜め込んで、完了後に取り出して集計する、今回のはこれで十分っぽい。

``` go
package main

import (
	"math/rand"
	"time"
	"fmt"
	"sync"
	"strconv"
)

func Sleep(i chan int) {
	r := rand.Intn(500) + 100 // 止まるミリ秒数の定義.ランダム数
	time.Sleep(time.Duration(r) * time.Millisecond) // 止まるミリ秒数の定義
	fmt.Printf("I Slept %d millseconds. \n", r)
	i <- r
}

func max(a []int) int {
	max := a[0]
	for _, i := range a {
		if i > max {
			max = i
		}
	}
	return max
}

func min(a []int) int {
	min := a[0]
	for _, i := range a {
		if i < min {
			min = i
		}
	}
	return min
}

func avg(a []int) int {
	l := len(a)
	sum := 0
	for _, i := range a {
		sum += i
	}
	return sum / l
}

func main() {
	rand.Seed(time.Now().UnixNano())
	// Sleep関数を2秒以内に50回実施して、停止した最大、最小、平均ミリ秒数を表示してください

	// チャネルを用意
	ch := make(chan int, 50)

	// Sleep関数を50回、並列処理で実行
	wg := &sync.WaitGroup{}
	for i := 0 ; i < 50 ; i++ {
		wg.Add(1)
		go func() {
			Sleep(ch)
			wg.Done()
		}()
	}

	// 全ての並行処理が完了するのを待つ
	wg.Wait()
	close(ch)

	// 並列で実行した結果を受け取る
	results := []int{}
	for v := range ch {
		results = append(results, v)
	}

	// 結果の集計
	fmt.Println("件数：" + strconv.Itoa(len(results)))
	fmt.Println("最大：" + strconv.Itoa(max(results)))
	fmt.Println("最小：" + strconv.Itoa(min(results)))
	fmt.Println("平均：" + strconv.Itoa(avg(results)))
}
```