How do goroutines compare to things like futures in eg. java?
The ultimate structure seems much the same: start off a worker, and then you wait for the result with a timeout. As an example, the following code gives the same result:
In Go:
c := make(chan Result)
go func() { c <- Web(query) } ()
go func() { c <- Image(query) } ()
go func() { c <- Video(query) } ()
for i := 0; i < 3; i++ {
result := <-c
results = append(results, result)
}
In Java:
ExecutorService executorService = Executors.newCachedThreadPool();
Future[] futures = new Future[]{
executorService.submit(new Web(query)),
executorService.submit(new Image(query)),
executorService.submit(new Video(query)),
};
for (int i = 0; i < 3; i++) {
Result result = futures[i].get();
results.add(result);
}
Goroutines are extremely lightweight. It is common to run hundreds of thousands of them on one machine. (The main reason for that is goroutines start off with a very small stack and extend it as necessary)
It is up to executorService to decide how many threads to use to execute those tasks. But they will be executed in threads. So you are unlikely to have more than a few hundred(maybe a few thousand?) of them around at any time.
Find an example that shows using a timeout channel along with another async activity on another channel, and then switching on that. Then you will start to see some elegance versus the Java way. (Sorry, in a hurry otherwise I'd provide the link myself :(
c = make(chan bool)
go doSomeWork(c)
select {
case b := <- c:
# do something here if something happened on the channel
case <- time.After(5 * time.Second):
# timed out :-(
}
Result result = future.get(5, TimeUnit.Second);
if (result == null)
// do something if timed out
else
// do something with result
My original question was phrased badly though, and what I meant to ask was: is the underlying implementation faster? Since Golang is designed as a systems language, is the channel/gorouting system a lot more performant than the Executor/future method of Java?
EDIT: I see what newobj is talking about now:
With the Java method, the 'task queue' in this case is very rigid and would be hard to add futures from multiple locations, while the goroutine method allows easy access to add new messages to the channel from anywhere.
You could probably emulate the goroutine method using a synchronized queue or similar, but the goroutine version handles it automatically.
FYI - you can also have 100,000 goroutines on a standard desktop without hitting resource problems (the go authors mentioned debugging a system in production that had 1.3 million). I doubt the same could be said for futures.
Dont know either very well, so this isn't an answer to your question..
But looking at your code; c := make(chan Result) is several orders of magnitude nicer than ExecutorService executorService = Executors.newCachedThreadPool(); =)
The ultimate structure seems much the same: start off a worker, and then you wait for the result with a timeout. As an example, the following code gives the same result:
In Go:
In Java: