I have two goroutines that each need to return their results to the main function. These results are of different types.
I have implemented a solution that uses a structure that holds these results. The structure is passed to the routines and each routine knows which field in the structure to use to save the results.
I am new to Golang and I am not sure if this is an elegant solution. Maybe channels are more suitable?
package main
import (
"fmt"
"sync"
)
type Results struct {
x float64
y int
}
func main() {
var r Results
var wg sync.WaitGroup
wg.Add(2)
go func(r *Results, wg *sync.WaitGroup) {
defer wg.Done()
r.x = 1.2
}(&r, &wg)
go func(r *Results, wg *sync.WaitGroup) {
defer wg.Done()
r.y = 34
}(&r, &wg)
wg.Wait()
fmt.Println(r)
}
2 Answers 2
Using a structure and waitgroups is silly for something this simple.
Just make two channels and fire off two goroutines. You don't care which one finishes first just read from both channels in whatever order is convenient (or even do something like someOtherFunction(<-c1, <-c2)
).
E.g.:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c1 := make(chan float64, 1)
c2 := make(chan int, 1)
go func() {
// simulate spending time to do work to get answer
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c1 <- 1.2
}()
go func() {
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
c2 <- 34
}()
x := <-c1
y := <-c2
fmt.Println(x, y)
}
-
\$\begingroup\$ you seem to imply that structures and a wait group are more "heavy" than channels. as a beginner I thought that this is the other way around. would you please elaborate more about this? why do you think so? \$\endgroup\$akonsu– akonsu2015年02月13日 22:36:10 +00:00Commented Feb 13, 2015 at 22:36
-
1\$\begingroup\$ It's not about time/overhead, it's about simplicity. I hope no one would bother to even do such a thing unless one or both of the tasks was time consuming in which case any minor overhead of one synchronizing method versus another is complete noise compared to the relative complexity of the resulting code. Simple is better. Here a pair of channels is the simplest thing to do and for this task perform more than adequately. \$\endgroup\$Dave C– Dave C2015年02月18日 11:11:21 +00:00Commented Feb 18, 2015 at 11:11
The only thing that you have to watch out for is that it would be easy for a give goroutine to access the wrong data field, and in a larger context this may be hard to catch. So for bigger, more complexed cases, I would suggest having a channel for each type of data that you want the goroutines to handle, and then passing the result back on a channel. If you've handled your types well, this will give you more robust code.
However, given the limited scope and low number of goroutines in this example, I think that this is an elegant way of handling the situation. Yes, you could have used channels, but in this case it would add complexity and get you very little. Nice clean code.
-
\$\begingroup\$ I disagree. Adding a struct and waitgroups and complicating to goroutine functions is in no way simpler or cleaner than just using a pair of channels. At minimum the structure and passing of
r
andwg
is pointless. \$\endgroup\$Dave C– Dave C2015年02月13日 22:01:15 +00:00Commented Feb 13, 2015 at 22:01
wg
directly and write tox
andy
(which you'd just declare as locals first). E.g. play.golang.org/p/dMDm6dWlOq \$\endgroup\$