Go语言中的惯用方式是使用goroutine(协程)。

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

Idiomatic Golang goroutines

问题

在Go语言中,如果我们有一个带有启动某个循环机制的方法(轮询A并永远执行B),最好的方式是:

// Run does stuff, you probably want to run this as a goroutine
func (t Type) Run() {
    // Do long-running stuff
}

并且在文档中说明这可能需要作为goroutine来启动(让调用者处理这个问题)。

或者将这个细节隐藏起来,不让调用者知道:

// Run does stuff concurrently
func (t Type) Run() {
   go DoRunStuff()
}

我对Go语言还不太熟悉,不确定惯例是让调用者在方法前加上"go"关键字,还是在代码设计为异步运行时自动加上。

我目前的观点是我们应该进行文档说明,并给调用者选择的权利。我认为在Go语言中,并发实际上并不是公开接口的一部分,而是使用接口时的一种属性。这样理解对吗?

英文:

In Go, if we have a type with a method that starts some looped mechanism (polling A and doing B forever) is it best to express this as:

// Run does stuff, you probably want to run this as a goroutine
func (t Type) Run() {
    // Do long-running stuff
}

and document that this probably wants to be launched as a goroutine (and let the caller deal with that)

Or to hide this from the caller:

// Run does stuff concurrently
func (t Type) Run() {
   go DoRunStuff()
}

I'm new to Go and unsure if convention says let the caller prefix with 'go' or do it for them when the code is designed to run async.

My current view is that we should document and give the caller a choice. My thinking is that in Go the concurrency isn't actually part of the exposed interface, but a property of using it. Is this right?

答案1

得分: 10

我对此事的看法一直是这样的,直到我开始为一个我想要并发处理的网络服务编写适配器。我有一个必须启动的go routine来解析从网络调用返回到通道的结果。在没有使用go routine的情况下,这个API绝对不会起作用。

然后我开始研究像net/http这样的包。在该包中,强制要求并发处理。在接口级别有文档说明它应该能够被并发使用,但默认实现会自动使用go routine。

因为Go的标准库通常在其自己的包中启动go routine,所以我认为如果你的包或API需要的话,你可以自己处理它们。

英文:

I had your opinion on this until I started writing an adapter for a web service that I want to make concurrent. I have a go routine that must be started to parse results that are returned to the channel from the web calls. There is absolutely no case in which this API would work without using it as a go routine.

I then began to look at packages like net/http. There is mandatory concurrency within that package. It is documented at the interface level that it should be able to be used concurrently, however the default implementations automatically use go routines.

Because Go's standard library commonly fires of go routines within its own packages, I think that if your package or API warrants it, you can handle them on your own.

答案2

得分: 9

我目前的观点是,我们应该对其进行文档化,并给调用者选择的权利。

我倾向于同意你的观点。

由于Go语言使得并发运行代码变得非常容易,你应该尽量避免在API中使用并发(这会强制客户端并发使用它)。相反,创建一个同步的API,然后客户端可以选择同步或并发地运行它。

几年前的一个演讲中讨论了这个问题:Twelve Go Best Practices

特别是第26页展示了更像你第一个示例的代码。

我认为net/http包是一个例外,因为在这种情况下,并发几乎是必需的。如果该包在内部不使用并发,客户端代码几乎肯定会使用并发。例如,http.Client(据我所知)不会启动任何goroutine,只有服务器会这样做。

在大多数情况下,对于调用者来说,无论哪种方式都只需要一行代码:

go Run()StartGoroutine()

同步的API在并发使用上并不更难,并且给调用者提供了更多的选择。

英文:

> My current view is that we should document and give the caller a choice.

I tend to agree with you.

Since Go makes it so easy to run code concurrently, you should try to avoid concurrency in your API (which forces clients to use it concurrently). Instead, create a synchronous API, and then clients have the option to run it synchronously or concurrently.

This was discussed in a talk a couple years ago: Twelve Go Best Practices

Slide 26, in particular, shows code more like your first example.

I would view the net/http package as an exception because in this case, the concurrency is almost mandatory. If the package didn't use concurrency internally, the client code would almost certainly have to. For example, http.Client doesn't (to my knowledge) start any goroutines. It is only the server that does so.

In most cases, it's going to be one line of the code for the caller either way:

go Run() or StartGoroutine()

The synchronous API is no harder to use concurrently and gives the caller more options.

答案3

得分: 1

因为情况各不相同,所以没有一个“正确”的答案。

显然,有些情况下,如果将实用程序、简单算法、数据集等打包为goroutines,可能会显得奇怪。

相反,有些情况下,自然而然地期望“底层”并发,比如一个功能丰富的IO库(显而易见的例子是HTTP服务器)。

对于一个更极端的情况,考虑一下如果你要制作一个插拔式并发服务的库。这样的API由每个模块组成,每个模块通过通道具有明确定义的接口。显然,在这种情况下,goroutines将不可避免地作为API的一部分启动。

一个线索可能是函数参数中通道的存在与否。但我希望能清楚地说明无论哪种情况都会有什么样的期望。

英文:

There is no 'right' answer because circumstances differ.

Obviously there are cases where an API might contain utilities, simple algorithms, data collections etc that would look odd if packaged up as goroutines.

Conversely, there are cases where it is natural to expect 'under-the-hood' concurrency, such as a rich IO library (http server being the obvious example).

For a more extreme case, consider you were to produce a library of plug-n-play concurrent services. Such an API consists of modules each having a well-described interface via channels. Clearly, in this case it would inevitably involve goroutines starting as part of the API.

One clue might well be the presence or absence of channels in the function parameters. But I would expect clear documentation of what to expect either way.

huangapple
  • 本文由 发表于 2015年2月14日 02:12:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/28506013.html
匿名

发表评论

匿名网友

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

确定