Reputation: 439
So besides handling multiple server requests is there any other time that concurrency is relevant? I ask because it's so built into the language that I feel wasteful if I don't use it but I can barely find a use for it.
Upvotes: 29
Views: 10796
Reputation: 9109
My 2 cents... If you think about channels/goroutines only in the context of concurrency, you are missing the boat.
While go is not an object language or strictly a functional language, it does allow you to take design features from both and apply them.
One of the basic tenets of object oriented design is the Single Responsibility Principle. Applying this principle forces you to think about design in terms of messages, rather than complex object behavior. These same design constraints can be used in go, to allow you to start thinking about "messages on channels" connecting single purpose functions.
This is just one example, but if you start thinking this way, you'll see many more.
Upvotes: 6
Reputation: 23789
Also not an expert at Go, so some of my approaches may be non-canonical, but here are some ways I've found concurrency useful so far:
Concurrency could be (read: "is sometimes, but not necessarily always") useful when you have operations that could run independently of each other but would otherwise run sequentially. Even if those operations depend on data or some sort of signal from other goroutines at certain points, you can communicate that all with channels.
For some inspiration and an important distinction in these matters, and for some funny gopher pictures, see Concurrency is not Parallelism.
Upvotes: 0
Reputation: 1803
Here is a good example from one of Go's inventors, Rob Pike, of using concurrency because it is an easier way to express the solution to a problem:
Generalizing on that a bit, any producer-consumer problem is a natural fit for 2 goroutines using a channel to pass outputs from the producer to the consumer.
Another good use for concurrency is interacting with multiple input/output sources (disks, network, terminal, etc.). Your program should be able to wake up and do some work whenever a result comes from any of these sources. It is possible to do this with one thread and a system call like poll(2) or select(2). When your thread wakes up, it must figure out which result came in, find where it left off in the relevant task, and pick up from there. That's a lot of code you need to write.
Writing that code is much easier using one goroutine per task. Then the state of that task is captured implicitly in the goroutine, and picking up where it left off is as simple as waking up and running.
Upvotes: 8
Reputation: 65859
Not an expert in Go
(yet) but I'd say:
Whenever it is easiest to do so.
The beauty of the concurrency model in Go
is that it is not fundamentally a multi-core architecture with checks and balances where things usually break - it is a multi-threaded paradigm that not only fits well into a multi-core architecture, it also fits well into a distributed system architecture.
You do not have to make special arrangements for multiple goroutines
to work together harmoniously - they just do!
Here's an example of a naturally concurrent algorithm - I want to merge multiple channels into one. Once all of the input channels are exhausted I want to close the output channel.
It is just simpler to use concurrency - in fact it doesn't even look like concurrency - it looks almost procedural.
/*
Multiplex a number of channels into one.
*/
func Mux(channels []chan big.Int) chan big.Int {
// Count down as each channel closes. When hits zero - close ch.
var wg sync.WaitGroup
wg.Add(len(channels))
// The channel to output to.
ch := make(chan big.Int, len(channels))
// Make one go per channel.
for _, c := range channels {
go func(c <-chan big.Int) {
// Pump it.
for x := range c {
ch <- x
}
// It closed.
wg.Done()
}(c)
}
// Close the channel when the pumping is finished.
go func() {
// Wait for everyone to be done.
wg.Wait()
// Close.
close(ch)
}()
return ch
}
The only concession I have to make to concurrency here is to use a sync.WaitGroup
as a counter for concurrent counting.
Note that this is not purely my own work - I had a great deal of help with this here.
Upvotes: 10