在golang中是否可以接受优美的封闭式并发模式?

在golang中是否可以接受优美的封闭式并发模式?

问题描述:

I've just started using concurrency in go. I have experience from concurrency in other languages and was saddened that go throws a panic if you're trying to write to a closed channel.

This pattern would have been really useful because you can decouple the lifecycle of actors and make them independent. This allows you to not have to synchronize the cleanup of them. Essentially I can let the reader close the channel before shutting down and let an arbitrary number of writers be notified and stop blocking (cancellation) via a write error on the channel.

I therefore wrote a generic function to handle this form of message passing:

/// Sends a message to a remote general channel.
/// Returns true if the message was sent (the send stopped blocking) or false if
/// the sender closed the channel.
/// When the channel is unbuffered, this function returning true means that the
/// sender has received the message and is acting on it.
func SendRemoteCmd(ch chan interface{}, msg interface{}) bool {
    defer func() {
        recover()
    }()
    ch <- msg
    return true
}

It works great, I'm just afraid that golang developers will get angry, call me and tell me they will "find me" when they read this code. There is also probably some good reason why the language gods has decided that this should be a panic in the first place. If that is the case, what design do you suggest instead?

Because sending to a closed channel is a program error, your channel sends to channels that can be closed must be synchronized. Generally the correct pattern here is to obtain a lock of some kind prior to attempting to send, if the channel can be closed by a third party.

This isn't particularly interesting until you're attempting to send on a channel that can be closed, as part of a select statement that involves other possible operations. In that case, a common pattern is to set channels to nil if operations on them shouldn't, or can't proceed. I have a very complex example of this in the connection.writeOptimizer function in my torrent client here.

Note that there is carefully considered ownership of resources involved in the write pipeline in the example, and this is a good way to prevent issues with for example, closing channels. writeOptimizer effectively owns the connection.writeCh, and signals downstream that no further data is coming by closing it. It's also the only goroutine that is sending to that routine, thereby avoiding to having to synchronize writes with the closing of the channel by some other means.