web-dev-qa-db-ja.com

Goが閉じたチャネルへの書き込みでパニックになるのはなぜですか?

Goが閉じたチャネルへの書き込みでパニックになるのはなぜですか?

value, ok := <-channelチャネルから読み取るためのイディオム。したがって、okの結果は、閉じたチャネルにヒットするかどうかをテストできます。

// reading from closed channel

package main

import "fmt"

func main() {
    ch := make(chan int, 1)
    ch <- 2
    close(ch)

    read(ch)
    read(ch)
    read(ch)
}

func read(ch <-chan int) {
    i,ok := <- ch   
    if !ok {
        fmt.Printf("channel is closed\n")
        return
    }
    fmt.Printf("read %d from channel\n", i)
}

出力:

read 2 from channel
channel is closed
channel is closed

Playground で「閉じたチャネルからの読み取り」を実行します

チャネルが閉じているときに単純に書き込もうとするとGoがパニックになるため、おそらく閉じたチャネルへの書き込みはより複雑です。

//writing to closed channel

package main

import (
    "fmt"
)

func main() {
    output := make(chan int, 1) // create channel
    write(output, 2)
    close(output) // close channel
    write(output, 3)
    write(output, 4)
}

// how to write on possibly closed channel
func write(out chan int, i int) (err error) {

    defer func() {
        // recover from panic caused by writing to a closed channel
        if r := recover(); r != nil {
            err = fmt.Errorf("%v", r)
            fmt.Printf("write: error writing %d on channel: %v\n", i, err)
            return
        }

        fmt.Printf("write: wrote %d on channel\n", i)
    }()

    out <- i // write on possibly closed channel

    return err
}

出力:

write: wrote 2 on channel
write: error writing 3 on channel: send on closed channel
write: error writing 4 on channel: send on closed channel

Playground で「closed channelへの書き込み」を実行します

私の知る限りでは、パニックすることなく、おそらく閉じたチャネルに書き込むための簡単なイディオムはありません。何故なの?読み取りと書き込みの間のこのような非対称的な動作の背後にある理由は何ですか?

14
Everton

Go Language Spec から:

チャネルcの場合、組み込み関数close(c)は、チャネルでこれ以上値が送信されないことを記録します。 cが受信専用チャネルの場合はエラーです。閉じたチャネルに送信したり閉じたりすると、ランタイムパニックが発生します。 nilチャネルを閉じると、ランタイムパニックが発生します。 closeを呼び出した後、以前に送信された値を受信した後、受信操作はブロックせずにチャネルのタイプのゼロ値を返します。多値受信操作は、チャネルが閉じているかどうかの表示とともに受信値を返します。

閉じたチャネルに書き込むと、プログラムがパニックになります。可能性があります このエラーをrecoverでキャッチ 本当にそうしたいのであれば、書き込み先のチャネルが開いているかどうかわからない状況にあるのは、通常、バグの兆候です。プログラムで。

いくつかの引用:

ここに動機があります:

チャネルの「クローズ」とは、実際にはチャネルでの特別な値の送信にすぎません。これは、これ以上値が送信されないことを約束する特別な値です。チャネルがクローズされた後に値を送信しようとすると、パニックになります。これは、実際に値を送信すると、クローズによって提供される保証に違反するためです。クローズは特別な種類の送信であるため、チャネルがクローズされた後のクローズも許可されません。

ここに別のものがあります:

チャネルクローズの唯一の用途は、これ以上価値がなくなることを読者に知らせることです。これは、値の単一のソースがある場合、または複数のソースが調整する場合にのみ意味があります。複数のゴルーチンが通信せずにチャネルを閉じるという合理的なプログラムはありません。これは、複数のゴルーチンが送信する値がないことを知っていることを意味します-通信しない場合、どのようにしてそれを決定できますか?

(イアンランステイラー)

-

ここに別のものがあります:

チャネルを閉じると、リソースとして解放されます。チャネルを複数回閉じることは、ファイル記述子を複数回閉じること、または割り当てられたメモリのブロックを複数回解放することよりも意味がありません。このようなアクションは、コードが壊れていることを意味します。そのため、閉じたチャネルを閉じるとパニックが発生します。

(ロブ・パイク)

-

出典: Go design detail rationale question-channel close

19
Justlike