在golang中,优雅的关闭写并发模式是否可接受?

huangapple go评论82阅读模式
英文:

Is a graceful close-write concurrency pattern acceptable in golang?

问题

我刚刚开始在Go语言中使用并发。我在其他语言中有并发的经验,但很遗憾的是,如果你试图向一个已关闭的通道写入数据,Go语言会抛出一个panic错误。

这种模式本来非常有用,因为它可以将actor的生命周期解耦,使它们相互独立。这样你就不需要同步它们的清理工作。实际上,我可以在关闭之前让读取者关闭通道,并通过通道上的写入错误通知和停止阻塞(取消)任意数量的写入者。

因此,我编写了一个通用函数来处理这种形式的消息传递:

/// 向远程通道发送消息。
/// 如果消息发送成功(发送不再阻塞),返回true;如果发送者关闭了通道,返回false。
/// 当通道是无缓冲的时候,函数返回true意味着发送者已经接收到消息并正在处理。
func SendRemoteCmd(ch chan interface{}, msg interface{}) bool {
    defer func() {
        recover()
    }()
    ch <- msg
    return true
}

这个函数工作得很好,只是我担心Go语言开发者看到这段代码后会生气,打电话给我并告诉我他们会“找到我”。而且很可能语言设计者之所以决定将这个问题作为panic错误,可能有一些很好的原因。如果是这样的话,你有什么其他的设计建议呢?

英文:

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 &lt;- 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?

答案1

得分: 2

因为向一个关闭的通道发送是一个程序错误,所以你的通道发送到可能被关闭的通道必须是同步的。通常,在尝试发送之前,正确的模式是先获取某种类型的锁,如果通道可以被第三方关闭。

直到你尝试在一个可以关闭的通道上发送,作为涉及其他可能操作的select语句的一部分时,这才变得特别有趣。在这种情况下,一个常见的模式是,如果对它们的操作不能或不应该继续进行,将通道设置为nil。我在我的种子客户端的connection.writeOptimizer函数中有一个非常复杂的例子,可以在这里找到。

请注意,在示例中,写入管道中的资源的所有权是经过仔细考虑的,这是防止关闭通道等问题的好方法。writeOptimizer有效地拥有connection.writeCh,并通过关闭它向下游发出没有更多数据的信号。它也是唯一一个向该例程发送数据的goroutine,从而避免了通过其他方式与通道的关闭同步写入的问题。

英文:

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.

huangapple
  • 本文由 发表于 2015年9月19日 05:01:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/32660980.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定