惯用的Golang goroutines

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?

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.

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.

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.