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.
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).
The most robust programming language in terms of concurrency That's reason enough to talk about Go and its clever way of dealing with concurrency. Go is known for its first-class support for concurrency, or the ability for a program to deal with multiple things at once.
In Go, concurrency works through the use of in-built functions known as Goroutines. Goroutines are functions, unique to Go, that run at the same time alongside other code or programs. They're not OS threads, though, they may be considered lightweight threads. Goroutines are deeply integrated with Go's runtime.
With Go, it's possible to do multi-threaded concurrency and parallelization with goroutines and goroutines work in an asynchronous way hence making use of both multi-threading and asynchronous programming efficiently.
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.
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:
Lexical Scanning in Go
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With