I've started looking more heavily into Go, and one of the things that surprised me is the semantics of channels. In particular, there seems to be a lot of confusion in the community about how to signal closure of a channel under various usage scenarios.
Try it :).
On Wed, Jul 25, 2012 at 7:37 PM, Marcelo Cantos wrote:I've started looking more heavily into Go, and one of the things that surprised me is the semantics of channels. In particular, there seems to be a lot of confusion in the community about how to signal closure of a channel under various usage scenarios.I wouldn't say there is a lot of confusion. There are people who insist on doing it incorrectly for the sake of brevity and there are people who write code that depends on the behavior of the current implementations, but it's exceedingly simple. A channel close operation is a send operation after which no other messages can be sent.
If a sender dies without knowing it, and thus unable to send a close, is this also reflected to the receiver? How?Alternatively: If the sender does not know that it has died, would the run-time system know it - and send the close on behalf of the goroutine (like closing all open files)?
The Emperor's Old ClothesCommunications of the ACM, Feb. 1981Hus Turing Award lecture,"One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies."(About ADA at the stage it was:)"Gradually these objectives have been sacrificed in favor of power, supposedly achieved by a plethora of features and notational conventions, many of them unnecessary and some of them, like exception handling, even dangerous."
There are a bunch of examples of this in go-nuts and other places(talks, std lib, blogs etc.) under request response chan.
This is not too hard to multiplex in a number of ways, for example a load balanced approach where new requests are routed to the first non-busy producer via a select.
close() cleans everything up nicely in a cascaded fashion, where the multiplexer detects the close and closes the request chans to the producers. This has the added benefit of not interrupting the in-flight requests (if you still care about them). I'll post some code examples later today.
@Marcello (sorry, can't quote ATM)
You can flip the idiom around, turning the producers into consumers and vice versa. Have the data generators hold the receive end of a channel of requests, where each request contains a chan to respond with data on (commonly referred to as the request-response chan idiom, or something).
When the data receiver closes the request chan the data generator will detect it via the val,ok := <- chan idiom.There are a bunch of examples of this in go-nuts and other places(talks, std lib, blogs etc.) under request response chan.
This is not too hard to multiplex in a number of ways, for example a load balanced approach where new requests are routed to the first non-busy producer via a select.
close() cleans everything up nicely in a cascaded fashion, where the multiplexer detects the close and closes the request chans to the producers. This has the added benefit of not interrupting the in-flight requests (if you still care about them). I'll post some code examples later today.
How about this ?
http://play.golang.org/p/PSpnGj632G
Uriel
Sorry, but I don't quite understand what "endrange()" means or how thinking this way solves the problems that are bothering me. Say I have five producers that don't know about each other, all writing into a single channel read by a filter, which passes transformed/filtered messages through to a downstream consumer. How do the five producers let the filter know when it (and, transitively, the downstream consumer) is not needed any more? What is the idiomatic solution for this very common scenario? Is it a separate control channel? Is it a channel per producer, which dramatically complicates the filter and still requires a control channel? Is it a HELLO-MSG1-MSG2-...-GOODBYE protocol? Just let the filter leak? None of the solutions I can think of are particularly palatable. So, what am I missing?
We could also introduce a statement "ok
= c <- v" that returns an indication of whether the value was sent,
and similarly a "case ok = c < -v:" in a select statement.
// Currentfor i := 0; ; i++ {select {case c <- fmt.Sprintf("%s %d", msg, i):// Do nothingcase <-quit:fmt.Println("Cleaning up...")quit <- true}}// Send returns bool
for i := 0; c <- fmt.Sprintf("%s %d", msg, i); i++ {}fmt.Println("Cleaning up...")done <- true