1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Goで並行処理練習(Goroutine,Channel)

Posted at

Golangによる並行処理

最近Golangを使って過去に書いたプログラムを書き直して勉強している。CライクでありCのめんどくさい記述が省けるとこに魅力を感じている。Golangは並行処理の記述が書きやすいらしい。では、手を出してみよう。ちなみに私は先輩にコードが汚い、センスがないと言われているので、見にくいかもです。これでも頑張ってます。

定積分の台形法による近似

今回Goroutineの練習に用いた題材は「定積分を台形法を用いて近似する」というもの。
アルゴリズムについては、ググってもらえばたくさん出てくる。いたってシンプルかつ簡単というのがGoroutine入門に向いているのではないか?的なノリで採択。

そして今回用いた数式はこちら。

\int_{0}^{1}\frac{4}{1+x^2}dx = \pi

計算の方針として、積分区間を任意の幅で分割し台形の計算を並行処理する。
日本語が下手くそすぎる故、コードをみてもらった方が早いです。

package main

import (
	"fmt"
	"sync"
)

const (
	p_num = 15   //区切りはば
	low   = 0.0  //積分区間
	high  = 1.0
)

func main() {
	ch := make(chan float32)
	var dx float32
	var x, sum float32
	var wg sync.WaitGroup
	point := make([]float32, p_num)
	dx = (high - low) / p_num
    x = low
	for i := 0; i < p_num; i++ {  //区切った時のXの値を記憶
		point[i] = x
		x += dx
	}

  for i := 0; i < p_num; i++ {
    wg.Add(1)
    go calculate(ch, i, point[i], dx, &wg)
    //go func(i int){
    //  wg.Add(1)
    //  fmt.Println(i)
    //  x := point[i]
    //  gx := 4 / (1.0 + x*x)
    //  hx := 4 / (1.0 + (x+dx)*(x+dx))
    //  ch <- (gx + hx) * dx / 2
    //  wg.Done()
    //}(i)
  }

  go func(){
    wg.Wait()
    close(ch)
  }()

  for v := range ch {
    sum += v
	}
	fmt.Println(sum)
}

func calculate(ch chan float32, i int, x, dx float32, wg *sync.WaitGroup) {
	gx := 4 / (1.0 + x*x)
	hx := 4 / (1.0 + (x+dx)*(x+dx))
	ch <- (gx + hx) * dx / 2
	wg.Done()
}

今回はmain関数の外に別でcalculate関数を用意しているが、コメントアウト部分のように、mainの中に並行処理部分を記述しても良い。しかし、注意する点として、変数iをgoroutineに参照させるために

go func(i int){
// do something
}(i)

と書く必要がある。
仮に書かなかったら、どうなるかをわかりやすい例で書くと

package main

import (
  "fmt"
  "time"
)


func main() {
  for i := 0; i < 10; i++ {
    go func(){
      fmt.Println(i)
    }()
  }
  time.Sleep(time.Second*5) //goroutineが始まる前にmainが終了するのを防ぐため
                            //WaitGroupeを用いるのがオススメ
}

/**********************
10
8
10
10
10
10
10
10
10
10
**********************/

うまく変数iの値が参照されない。iを参照するように変更すると

package main

import (
  "fmt"
  "time"
)


func main() {
  for i := 0; i < 10; i++ {
    go func(i int){
      fmt.Println(i)
    }(i)
  }
  time.Sleep(time.Second*5) //goroutineが始まる前にmainが終了するのを防ぐため
                            //WaitGroupeを用いるのがオススメ
}

/**********************
1
0
7
6
8
9
3
5
4
2
**********************/

以上

はまったポイント

今回の練習ではまったポイントと言えば、WaitGroupeを用いて同期を取っているのだが、
全てのGoroutineがdeadlockしたよってpanicメッセージが飛んでくることがあった。これは、Wait()
もGoroutine化してあげることで解決。

go func(){
  wg.Wait()
  close(ch)
}()

全てのGoroutineが完了したらデータ受け渡し用のchannelをcloseしてあげることで、

  for v := range ch {
    sum += v
    }

を用いてchannelの中の値をエラーなくrangeで参照することができる。
closeせずにchannelから値を参照しようとすると、エラーが発生する。これは少し手間取った。
以上

おわりに

次は、チャットアプリをTCPを用いてごにょごにょしているから、完成したら投稿しようと思う。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?