質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

505閲覧

Go言語でゴルーチンを使う際のイディオム

退会済みユーザー

退会済みユーザー

総合スコア0

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2019/04/29 05:57

Go言語でゴルーチンを使う際の
イディオムというか以下のような処理がバッドノウハウかどうかをお聞きします。

go

1 2package main 3 4import ( 5 "fmt" 6 "math/rand" 7 "sync" 8 "time" 9) 10 11var wait *sync.WaitGroup 12var commonBool bool 13 14func main() { 15 wait = &sync.WaitGroup{} 16 // Need to wait 1 delta count. 17 wait.Add(1) 18 go HeavyFunction(wait, &commonBool) 19 wait.Wait() 20 if commonBool == true { 21 fmt.Println("Succeeded completing Goroutine. ") 22 } else { 23 fmt.Println("Failed completing Goroutine. ") 24 } 25} 26 27// Quite function which has heavy process. 28func HeavyFunction(w *sync.WaitGroup, b *bool) { 29 30 // It is a process to wait 5 seconds. 31 time.Sleep(time.Second * 5) 32 fmt.Println("Completed waiting 5 seconds.") 33 34 // Notice completing subroutine to main thread. 35 var r *rand.Rand = rand.New(rand.NewSource(150)) 36 r.Seed(time.Now().Unix()) 37 var i int = r.Intn(9) 38 if (i % 2) == 0 { 39 // Success when getting zero. 40 *b = true 41 w.Done() 42 } else { 43 // Fail when getting not zero. 44 *b = false 45 w.Done() 46 } 47}

HeavyFunction という5秒ほどかかる処理があったとして、
同関数内で、仮に偶数の場合true、奇数の場合falseとします。
その場合、*sync.WaitGroup.Done();でゴルーチンを終了させ、main関数内でゴルーチンの成功あるは失敗を引数に渡したグローバル変数のポインタで判断したのですが、これはプロダクションとして利用してよいのものでしょうか?

また、これをWaitGroupではなくChannelを利用した場合は

go

1// channelを利用した場合 2package main 3 4import ( 5 "fmt" 6 "math/rand" 7 "sync" 8 "time" 9) 10 11var wait *sync.WaitGroup 12var commonBool bool 13 14// チャンネルの場合 15var ch chan int 16var result chan bool 17 18func main() { 19 result = make(chan bool) 20 go HeavyFunctionUsingChannel(result) 21 22 // 23 var res bool 24 res = <-result 25 if res == true { 26 fmt.Println("Succeeded completing Goroutine. ") 27 } else { 28 fmt.Println("Failed completing Goroutine. ") 29 } 30} 31 32 33func HeavyFunctionUsingChannel(result chan bool) { 34 35 // Heavy Process. 36 time.Sleep(time.Second * 5) 37 fmt.Println("Comleted waiting 5 seconds using channel.") 38 39 var r *rand.Rand = rand.New(rand.NewSource(2434)) 40 r.Seed(time.Now().Unix()) 41 if (r.Intn(9) % 2) == 0 { 42 result <- true 43 } else { 44 result <- false 45 } 46} 47

上記のように var result chan bool というchannelで成功あるいは失敗とゴルーチンの終了そのものを兼用しています。

よりよいグッドノウハウがあればご教授をお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

WaitGroup変数をグローバルにおくのは控えましょう。
WaitGroupは値型でもポインタ型でも使えるのですが、
引数で渡す際はポインタ型である必要があります。
が、引数で渡す状況は以下のように書くことで回避できます。
以下の形にすればmain関数のスコープにwait変数を閉じ込めることができ、
waitの使い所が一目瞭然です。

go

1func main() { 2 var wait sync.WaitGroup 3 // Need to wait 1 delta count. 4 wait.Add(1) 5 go func() { 6 defer wait.Done() 7 HeavyFunction(&commonBool) 8 } () 9 wait.Wait() 10 if commonBool == true { 11 fmt.Println("Succeeded completing Goroutine. ") 12 } else { 13 fmt.Println("Failed completing Goroutine. ") 14 } 15}

あと以下のように2度「w.Done()」を書くとWaitGroupを正しく操作しているかどうかを
ロジックを追う必要が出てきますので、HeavyFunctionの先頭で「defer w.Done()」と書く方が誤解がないでしょう。(そもそも前述のように書けば引数でWaitGroupを引き廻す必要もありません)

go

1func HeavyFunction(w *sync.WaitGroup, b *bool) { 2 if ... { 3 ... 4 w.Done() 5 } else { 6 ... 7 w.Done() 8 } 9}

チャネルバージョンはチャネルをmain内で宣言すればグローバル宣言は不要なはず。

あと全体に言えることですが、単独の処理をgoroutineで起動して直後に完了を待ち続けているというのは
実際のところgoroutineを使うことのメリットはありません(goキーワードをなくすことが出来、そのほうが効率が良い)が、練習ということであれば問題はありません。

投稿2019/04/29 14:32

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2019/04/29 14:34

もしプロダクションとしてこのコードのPRが上がってきたらgoroutineを使うのをやめさせるでしょう。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問