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

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

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

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

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Q&A

解決済

1回答

1001閲覧

2以上の値を返す関数(サブルーチン)の一つだけを使って select-caseで使えるようにしたい

TomMurphy

総合スコア13

Go

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

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

0グッド

0クリップ

投稿2021/12/04 16:43

#質問の背景

受信専用のgoroutineを作って、for-select-caseを使って各種の受信の多面待ちを行いたいと思っております。

今回の場合sync.Condを使ったbroadcast→Waitを使っているのですが、これがselect-caseでは取れないようなので、2つの返り値を作って、これに対応しようと考えています。
(もし、取れる方法がありましたら、この質問はそちらに変更とさせて下さい)

#実施したい事項

func (bc *BroadCaster) Recv() (int, chan interface{}) { bc.cond.L.Lock() defer bc.cond.L.Unlock() bc.ch <- 1 bc.cond.Wait() return bc.id, bc.ch }

というような感じにして、チャネル用の返り値(bc.ch)を追加して対応しようとしました。

#検証した実験結果

まず、返り値が「1つ」である場合、以下のようなコードで目的が達成できそうなことは確認しました。

package main import ( "fmt" ) func returnChannel() <-chan string { message := make(chan string, 1) message <- "hello world again" return message } func testChannel() <-chan string { message := make(chan string, 1) message <- "good-by world" return message } func main() { /* b := returnChannel() fmt.Println(<-b) fmt.Println(<-returnChannel()) */ select { case s := <-testChannel(): fmt.Println(s) case v := <-returnChannel(): fmt.Println(v) } }

(このコードでは、問答無用で、case s: の方が選ばれますが、機能確認用であることをご理解下さい)

#ご教示頂きたい事項
(1)このように、2つ以上の返り値を持つ関数(サブルーチン)の、特定の一つだけ(この場合は、channelだけ)を取り出して、select-case で使う方法があれば、ご教示頂けますよう、何卒よろしくお願い申し上げます。

(もしsync.Condを、他のチャネルと合わせて、select-caseで使える方法を御存じでしたら、何卒そちらの方もご教示頂けますようお願い致します)

利用環境

go version go1.17 windows/amd64
Visual Studio Code バージョン: 1.62.3 (user setup)

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

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

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

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

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

guest

回答1

0

ベストアンサー

  • sync.Cond.Waitを利用するのは待ち専用goroutineが必須です。
  • チャネルに載せ替えるには上記待ち専用goroutineからチャネルに通知を載せ別のgoroutineで受ける必要があります。

つまり、チャネルに載せ替える=ひとつgoroutineを余計に作る必要があり、これはsync.Cond.Broadcastで低コストにメッセージ配信しようという意図もあまり意味がなくなってしまいます。

ここで、もしブロードキャストにはチャネルを用いたPubSubで実現した場合はどうなるでしょう?

  • チャネルでメッセージが到達するのでselectで待受しやすい
  • 余計なgoroutineを作らずに済む

というわけで、やりたい事に適したブロードキャスト方法はPubSubだったということになります。

追記

コメントに頂いたコードはチャネルの容量を1に設定することでデッドロックしなくなります。

go

1package main 2 3import "fmt" 4 5func channel_test() chan int { 6 ch := make(chan int, 1) 7 ch <- 1 8 return ch 9} 10 11func main() { 12 v := channel_test() 13 fmt.Println(v) 14}

また、PubSub実装は

https://mattn.kaoriya.net/software/lang/go/20131225102008.htm

こちらなどを参考にされてみてはどうでしょうか?

投稿2021/12/05 13:11

編集2021/12/06 01:59
nobonobo

総合スコア3367

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

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

TomMurphy

2021/12/06 01:34

ご回答頂きありがとうございます。 今回の目的は、ブロードキャストもチャネルも両方とも受信専用スレッド1つでselect case を使って待受させたい、というのが目的でしたが、どうやら、そういうことはできそうにない、ということが分かってきました。 ``` package main import "fmt" func channel_test() chan int { ch := make(chan int) ch <- 1 return ch } func main() { v := channel_test() fmt.Println(v) } ``` が、 fatal error: all goroutines are asleep - deadlock! になることを確認しましたので、そもそも、チャネルを返り値とするサブルーチンは作れない(go channel_test() とすれば動く)と分かりました。 ご指摘頂いた通り、ブロードキャスト用のgoroutineをもう一つ追加することで対応したいと思います。 ありがとうございました。
TomMurphy

2021/12/06 07:10

"ch := make(chan int, 1)" は、盲点でした。 「送信ロック」がある、というのを、最近どっかで読んだような気がしますが、さっぱり失念しておりました。ご指摘、ありがとうございました。 (まだselect-case を、踏んばってみる余地あるかな?) また、前回ご教示頂いたPubSubのコードは、大切に活用させて頂いております。 また、今回のhttps://mattn.kaoriya.net/software/lang/go/20131225102008.htm の内容も、早速試させて頂きました。telnet というのが、なんとも懐しくて楽しかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問