web-dev-qa-db-ja.com

Goを使用して、チャネルに既読値があるかどうかを確認する

チャネルに読み取る値があるかどうかを確認するにはどうすればよいですか?

チャンネルを読んでいるときにブロックしたくない。価値があるかどうか知りたい。ある場合は読みます。 (まだ)ない場合は、別のことを行い、後でもう一度確認します。

31
gonewbgo

[〜#〜] warning [〜#〜]:これは正確ではなくなりました。以下の回答を参照してください。

ドキュメントから:

受信式がフォームの割り当てまたは初期化で使用されている場合

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch

受信操作は非ブロッキングになります。操作が続行できる場合、ブール変数okがtrueに設定され、値がxに格納されます。それ以外の場合、okはfalseに設定され、xはそのタイプのゼロ値に設定されます

5
nos

私が知っている、チャネルから読み取る非ブロッキング操作は select block の中にあり、デフォルトのケースがあります:

    select {
    case x, ok := <-ch:
        if ok {
            fmt.Printf("Value %d was read.\n", x)
        } else {
            fmt.Println("Channel closed!")
        }
    default:
        fmt.Println("No value ready, moving on.")
    }

ここでノンブロッキングを試してください

以前の回答についての注記:受信演算子自体がブロック操作になります 、Go 1.0.3以降。 spec が変更されました。 ここでブロッキングを試してください (デッドロック)

80
Deleplace

これを頻繁に行う場合は、おそらく優れたデザインではないので、チャネルから読み取るものが何もないときに、計画している作業を行うために別のゴルーチンを生成する方がよいでしょう。 Goのチャネルの同期/ブロックの性質により、コードが読みやすくなり、推論が容易になりますが、スケジューラと安価なゴルーチンは、待機しているゴルーチンがリソースをほとんど消費しないため、非同期呼び出しが不要であることを意味します。

7
Jessta

少なくとも、同期(バッファリングされていない)チャネルは必要ありません。チャネルから値を取得するように要求せずに値が待機しているかどうかを確認する方法はありません。

バッファリングされたチャネルの場合、技術的にはlen関数を使用して、説明したことを実行できますが、実際にはそうすべきではありません。あなたのテクニックは無効です。

これは、競合状態を表すためです。チャネルchが指定されている場合、ゴルーチンはlen(ch)> 0を確認し、待機中の値があると結論付ける可能性があります。ただし、ブロックせずにチャネルから読み取ることができると結論付けることはできません。lenをチェックしてから受信操作が実行されるまでの間に別のルーチンがチャネルを空にする可能性があります。

あなたが説明した目的のために、Ripounetが示したようにデフォルトのケースでselectを使用してください。

7
Sonia

残念ながら、以前の答えは正しくありません。 spec は、len()関数を使用してこの方法でチャネルを使用できることを明確に示していますが、チャネル容量-チャネルのバッファ長を指定している場合のみです。作成中にチャネル容量を省略した場合-チャネル操作は常にブロックされます。

5