channelのことを改めて調べたら、closeとclosedなんていう性質があることに気付いた。
- channelはcloseすると以降のsendが無視されるようになる
- そのあとそのchannelから一回値をreceiveすると、以降closedによる判定がtrueになる
- 最後のreceiveで返ってくる値は0
サンプル
package main
import (
"fmt"
)
type Message int
const (
MSG_CLOSE Message = 0
)
type Output struct {
data int
response Response
}
type Response int
const (
RESPONSE_OK Response = 0
)
type DataWithResponse struct{
data int
responseChannel chan Output
}
func Processor(msgChannel chan Message, inputChannel chan DataWithResponse) {
defer func(){
close(msgChannel)
close(inputChannel)
}()
processing := true
for processing {
select {
case msg := <- msgChannel:
if msg == MSG_CLOSE {
processing = false
}
case input := <- inputChannel:
fmt.Println("Processing:",input.data)
// process input here...
outputData := input.data * 2
input.responseChannel <- Output {
outputData,
RESPONSE_OK,
}
}
}
}
func main() {
msgChannel := make(chan Message)
inputChannel := make(chan DataWithResponse)
go Processor(msgChannel, inputChannel)
// send input
outputChannel := make(chan Output)
input := DataWithResponse {
123,
outputChannel,
}
inputChannel <- input
// receive output
output := <- outputChannel
fmt.Println("Processed:",output.data)
if closed(msgChannel) {
fmt.Println("Message channel closed 1")
}
msgChannel <- MSG_CLOSE
// see what happens if we try to input more
// while the channel is about to get closed
inputChannel <- input
msgChannel <- MSG_CLOSE
inputChannel <- input
msgChannel <- MSG_CLOSE
inputChannel <- input
msgChannel <- MSG_CLOSE
if closed(msgChannel) {
fmt.Println("Message channel closed 2")
}
msgClosed := <- msgChannel
if msgClosed == MSG_CLOSE {
fmt.Println("Closed:", msgClosed)
}
if closed(msgChannel) {
fmt.Println("Message channel closed 3")
}
}
出力
$ ./channel-test Processing: 123 Processed: 246 Closed: 0 Message channel closed 3
出力を見ればわかるように、きっちり仕様通りになっている。closeが発行されたであろうタイミングでchannelに入力を入れてもブロックしてないし、closeされたchannelから値を読み出してはじめてclosedになってる。closeしたあとのchannelは値の0を返すようなので、CLOSE状態を判定する定数を0にしておけば可読性も良い。
ということでchannelのcloseは積極的に使いましょう。
おまけ
さっき気付いたけど、closeしたchannelにsendしすぎるとpanicが発生する。変更点
for {
// see what happens if we try to input more
// while the channel is about to get closed
inputChannel <- input
msgChannel <- MSG_CLOSE
inputChannel <- input
msgChannel <- MSG_CLOSE
inputChannel <- input
msgChannel <- MSG_CLOSE
}
出力
$ ./channel-test Processing: 123 Processed: 246 throw: too many operations on a closed channel panic PC=0xb7615f14 (以下略)
0 件のコメント:
コメントを投稿