読み取り専用チャネルの主な動機は、チャネルの破損とパニックを防ぐことだと思います。によって返されたチャネルに書き込むことができると想像してくださいtime.After
。これにより、多くのコードが台無しになる可能性があります。
また、次の場合にパニックが発生する可能性があります。
- チャンネルを複数回閉じる
- 閉じたチャネルに書き込む
これらの操作は、読み取り専用チャネルのコンパイル時エラーですが、複数の go-routine がチャネルを書き込み/閉じることができる場合、厄介な競合状態を引き起こす可能性があります。
これを回避する 1 つの方法は、チャネルを閉じずにガベージ コレクションを実行することです。ただし、close
クリーンアップのためだけではなく、実際にはチャネルが範囲外の場合に使用されます。
func consumeAll(c <-chan bool) {
for b := range c {
...
}
}
チャネルが閉じられない場合、このループは終了しません。複数の go-routine が 1 つのチャネルに書き込みを行っている場合、どれがチャネルを閉じるかを決定するために、多くの簿記が必要になります。
読み取り専用チャネルを閉じることはできないため、これにより正しいコードを記述しやすくなります。@jimt が彼のコメントで指摘したように、読み取り専用チャネルを書き込み可能チャネルに変換することはできないため、チャネルの書き込み可能バージョンにアクセスできるコードの部分のみがチャネルを閉じる/書き込むことができることが保証されます。
編集:
複数の読者を持つことに関しては、あなたがそれを説明している限り、これはまったく問題ありません。これは、生産者/消費者モデルで使用する場合に特に役立ちます。たとえば、接続を受け入れてワーカー スレッドのキューに書き込むだけの TCP サーバーがあるとします。
func produce(l *net.TCPListener, c chan<- net.Conn) {
for {
conn, _ := l.Accept()
c<-conn
}
}
func consume(c <-chan net.Conn) {
for conn := range c {
// do something with conn
}
}
func main() {
c := make(chan net.Conn, 10)
for i := 0; i < 10; i++ {
go consume(c)
}
addr := net.TCPAddr{net.ParseIP("127.0.0.1"), 3000}
l, _ := net.ListenTCP("tcp", &addr)
produce(l, c)
}
接続処理は、新しい接続を受け入れるよりも時間がかかる可能性が高いため、単一のプロデューサーで多くのコンシューマーを使用する必要があります。複数のプロデューサーはより困難ですが (チャネルを閉じる人を調整する必要があるため)、チャネル送信にある種のセマフォ スタイルのチャネルを追加できます。