Go uses goroutines to execute multiple bits of code at the same time. Channels allow for the aggregation of the results of these concurrent calls after they have finished.
Consider a case where we want to make several GET requests to a server. The server takes some time to process each request, in many cases can handle many simultaneous connections. In a language like Python, we might do the following to make several requests:
We run the server to accept incoming requests:
go run server.go
Then we run the Python script:
python client.py
which outputs the following:
The count is: 1The count is: 2The count is: 3The count is: 4The count is: 5The count is: 6The count is: 7The count is: 8The count is: 9The count is: 10Time elapsed: 0.96 seconds
Our server output looks something like this:
Sleeping for: 120ms
The count is: 1Sleeping for: 188ms
The count is: 2Sleeping for: 132ms
The count is: 3Sleeping for: 87ms
The count is: 4Sleeping for: 84ms
The count is: 5Sleeping for: 137ms
The count is: 6Sleeping for: 13ms
The count is: 7Sleeping for: 31ms
The count is: 8Sleeping for: 19ms
The count is: 9Sleeping for: 60ms
The count is: 10
The total elapsed time is about what we would expect. Ten requests at approximately 100ms each, gives us about one second for all requests. However, our Python script spends most of its time waiting for a response from the server which is sleeping. What would happen if we kicked off all ten requests to the server at the same time? We might expect this to be a problem. After all, if Python only can make one request at a time, why should our server be able to process more than one request at a time. For this to work in Python we need to use something like uWSGI or asyncio. It turns out that Go’s builtin net/http library uses goroutines to handle multiple incoming requests at once. Let’s trying making our requests in a similar manner, using goroutines to kick off all the requests at once, with the following Go code: