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

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

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

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

Q&A

解決済

1回答

1645閲覧

golangの標準入力をgoroutineにて動かしたときの挙動について

Elmogawa

総合スコア6

Go

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

0グッド

0クリップ

投稿2022/04/10 09:37

編集2022/04/10 09:43

概要

初めまして。
Golang初心者です。

Gopher道場の課題の「タイピングゲームを作ろう」にて、標準入力を行う際に、挙動がどうなっているかわからないため、どなたかに教えていただきたく、質問させていただきました。

自分が考えたゲームの流れ

ゲーム性としてはおかしいですが、以下の感じで考えました。

名前入力 -> 制限時間入力 -> カウントダウン開始 -> タイピングゲーム

コード

以下ざっくりと現状作ったコードになります。
まだ、上記のタイピングゲームの部分は完成していません。

go

1package main 2 3import ( 4 "bufio" 5 "fmt" 6 "io" 7 "os" 8 "strconv" 9 "time" 10) 11 12// TypingGameを扱う情報 13type GameInfo struct { 14 timeLimit time.Duration 15 score int 16 name string 17} 18 19func main() { 20 // 初期化 21 g := GameInfo{time.Duration(10 * time.Second), 0, "defalut",} 22 stdin := os.Stdin 23 24 // 名前入力 25 done := makeDoneChan() 26 fmt.Print("名前を入力してね!(何も入力しない場合は defalut になります)\n > ") 27 name := input(stdin, done) 28 res := <-name 29 if res != "" { 30 g.name = res 31 } 32 fmt.Println(g.name, "さんようこそ!") 33 // サブルーチンを終了させる 34 close(done) 35 36 // タイムリミットの設定 37 fmt.Print("タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) \n > ") 38 done = makeDoneChan() 39 timeLimit := input(stdin, done) 40 num := <-timeLimit 41 // サブルーチンを終了させる 42 close(done) 43 if v, err := strconv.Atoi(num); err == nil && v > 0 { 44 g.timeLimit = time.Duration(v) 45 } 46 fmt.Printf("タイムリミットは %v です!\n", g.timeLimit) 47 48 49 // カウントダウン開始 50 countdown() 51 52 // ゲーム開始 53 54} 55// goroutine終了用のチャネルを返す 56func makeDoneChan() (chan struct{}) { 57 return make(chan struct{}) 58} 59 60// 3秒間カウントダウンを行うコード 61func countdown() { 62 for i := 3; i > 0; i-- { 63 fmt.Printf("%d ", i) 64 time.Sleep(time.Second) 65 } 66 fmt.Println("Go!") 67} 68 69// func game() { 70// words, err := questions() 71// if err != nil { 72// fmt.Println("エラーが起きたため、ゲームを終了します。") 73// os.Exit(1) 74// } 75 76// // words 77// } 78 79// 自分で作成したtextファイルを読み込む 80func questions() ([]string, error) { 81 var words []string 82 f, err := os.Open("question.txt") 83 if err != nil { 84 return nil, fmt.Errorf("%v", err) 85 } 86 s := bufio.NewScanner(f) 87 for s.Scan() { 88 words = append(words, s.Text()) 89 } 90 return words, nil 91} 92 93func input(r io.Reader, done chan struct{}) <-chan string { 94 ch := make(chan string) 95 go func() { 96 s := bufio.NewScanner(r) 97 defer close(ch) 98 for s.Scan() { 99 select { 100 case <-done: 101 return 102 case ch <- s.Text(): 103 } 104 } 105 }() 106 return ch 107} 108

実行結果とわからない箇所

パターン1と2から、タイムリミット入力時には一度改行しないと、欲しい入力結果を得ることができません。
なぜ、このようなことが起こるのかを教えていただければ幸いです。
よろしくお願い致します。

パターン1

bash

1名前を入力してね!(何も入力しない場合は defalut になります) 2 > nanashi 3nanashi さんようこそ! 4タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) 5 > 20 6 7タイムリミットは 10s です! 83 2 1 Go!

パターン2

bash

1名前を入力してね!(何も入力しない場合は defalut になります) 2 > nanashi 3nanashi さんようこそ! 4タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) 5 > 620 7タイムリミットは 20s です! 83 2 1 Go!

最後に

go始めたばかりなんで、かなりコードが汚いと思いますが、その部分は見逃していただきたいです。
また、きたないと思う部分も同時に指摘していただけると助かります。

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

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

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

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

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

melian

2022/04/10 12:40 編集

> なぜ、このようなことが起こるのか input 関数内にある goroutine で、 case ch <- s.Text(): としていますが、この直後に for ループに戻って s.Scan() が実行されるので入力待ちになっています。 そして、その時点で何かを入力すると done が受信されて channel が閉じます(goroutine が終了)。 この場合、読み込むのは一行なので channel に入力内容を送信したら return する必要があります(goroutine を終了させる)。 case ch <- s.Text(): return
Elmogawa

2022/04/10 13:02 編集

s.Scanの挙動がわからなかったのですが、melianさんのコメントを見て、スッキリしました! ありがとうございます! 解決できたので、ぜひベストアンサーに選ばせてください!
melian

2022/04/10 13:13

ありがとうございます。回答に転記しました。
guest

回答1

0

ベストアンサー

※ コメント欄より転記

なぜ、このようなことが起こるのか

input 関数の goroutine 内で、

go

1 case ch <- s.Text():

としていますが、この直後に for ループに戻って s.Scan() が実行されるので入力待ちになっています。そして、その時点で何かを入力すると select が実行されて done が受信されます。ここで channel が閉じます(goroutine が終了)。
この場合、読み込むのは一行なので channel に入力内容を送信したら return する必要があります(goroutine を終了させる)。

go

1 case ch <- s.Text(): 2 return

※ 実を言いますと、この場合の処理では goroutine も channel も必要ないのではないかな〜と思います

投稿2022/04/10 13:12

melian

総合スコア19796

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

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

Elmogawa

2022/04/10 13:17

ご回答・ご指摘ありがとうございます! 無理矢理goroutineを使っていましたが、順に処理しているだけなので、確かにいらないですね。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問