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

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

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

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

Q&A

解決済

1回答

4289閲覧

forの中でselectを回し続ける場合のチャネルの使い方の例を教えてください!

退会済みユーザー

退会済みユーザー

総合スコア0

Go

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

0グッド

1クリップ

投稿2018/05/19 21:26

編集2018/05/19 23:54

#例文と質問
例えば

go

1func test() { 2 for { 3 select { 4 case v, ok := <-ch: 5 if ok { 6 fmt.Println(v) 7 } else { 8 fmt.Println("close") 9 } 10 default: 11 } 12 } 13}

このようなtest関数を作り、ループで待機させてここに別のgoroutine(例えばmain関数)からどんどんchannel経由で値を送っていき、それを裁かせるようにしたいのですが、そういう場合他のgoroutineからの値の送信はどのようにすれば一番合理的でしょうか。

#考えたこと

グローバル変数を使って

go

1var ch = make(chan string) 2 3func main() { 4 go test() 5 ch <- "Hello" 6 ch <- "2" 7 ch <- "teratail" 8} 9 10func test() { 11 for { 12 select { 13 case v, ok := <-ch: 14 if ok { 15 fmt.Println(v) 16 } else { 17 return 18 } 19 default: 20 } 21 } 22}

とすれば、test関数からもmain関数からもch変数を参照できて楽なのですが、どうもグローバル変数を簡単に作るのはよくない気がします。
このように複数のgoroutine同士のやりとりの際のチャネルの参照方法について、グローバル変数を使ってもいいのか、もしくはできるだけスコープを狭めるようにした実装がいいのか。また後者なら方法をご教授ください。
お願いいたします...

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

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

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

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

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

guest

回答1

0

ベストアンサー

2点修正をおすすめします。

1点目はselectにdefaultケースをつけてしまうと、ほかのケースが成立しない間は
ビジーループ(CPU資源の浪費)になってしまうので避けましょう。

2点目はtest関数内に必要なものは引数で渡す方が望ましいと思います。

修正を反映すると以下のようになります。
これで広域変数に依存しなくなり、テストコードが書きやすくなるでしょう。

https://play.golang.org/p/zDGlCsEFiKa

go

1func main() { 2 ch := make(chan string) 3 go test(ch) 4 ch <- "Hello" 5 ch <- "2" 6 ch <- "teratail" 7} 8 9func test(ch <-chan string) { // 読み出し専用と宣言する時、<-をchanの前につける 10 for { 11 select { 12 case v, ok := <-ch: 13 if ok { 14 fmt.Println(v) 15 } else { 16 return 17 } 18 } 19 } 20}

ただし、selectにケースが1つしかない場合はselect構文を使う必要はありません。

for文以下を整理すると以下のようになります。

go

1for { 2 v, ok := <-ch: 3 if ok { 4 fmt.Println(v) 5 } else { 6 return 7 } 8}

Goのifによる分岐はearlyリターンが推奨されているので更に整理すると・・・。

go

1for { 2 v, ok := <-ch: 3 if !ok { 4 return 5 } 6 fmt.Println(v) 7}

Goのforには実は上記と等価な構文がありまして・・・。

go

1for v := range ch { 2 fmt.Println(v) 3}

というように書けます。

追記

また、goroutineとして動作を開始するtest関数が正常に終了するためには、
test関数以外のgoroutineからclose(ch)を呼ぶ必要があります。
現在の実装はmainのgoroutineが終了したことによりプロセスが終了することで
強制的に他のgoroutineが停止している状況です。

投稿2018/05/21 10:07

編集2018/05/22 01:01
nobonobo

総合スコア3367

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

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

nobonobo

2018/05/21 10:13

ちなみに広域変数と書いたのはわけがあり、GoではC言語で言うところのグローバル変数と言うものが作れません。必ずパッケージ単位で分離した名前空間にマップされます。
退会済みユーザー

退会済みユーザー

2018/05/21 22:03

とても丁寧な回答ありがとうございます; _ ; 仰るとおりdefaultをつけないならselectにしなくても問題ありませんね! また、チャネルをrangeで回せることも初めて知りました とてもわかりやすく勉強になりました。ありがとうございました!
退会済みユーザー

退会済みユーザー

2018/05/22 11:57

closeを呼ばなかった場合(正常終了させなかった場合)どのような事態になりますか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問