Understanding Golang and Goroutines
This article is focussed on understanding the basics of Golang. We will talk about the performance aspect of Golang and we will try to scale up the application by creating simple Goroutines.
We will also focus on underlying execution logic and what makes Golang different from other languages.
Concurrency in Golang
Before we proceed with the article, we need to understand the concept of parallelism and concurrency. Golang is capable of bringing both concurrency and parallelism to the application.
Let's understand the basic difference.
Concurrency means that the application has more than one thing to do at the same time. It's about creating multiple processes executing independently.
Applications might be dealing with lots of processes together to accomplish the intended behavior. Let's assume a simple ecommerce website and evaluate the list of concurrent tasks that it needs to execute:
- Running a banner on the top with the latest deals and products.
- Display the count of currently online users of the website.
- Update the user cart details when products are selected.
- Keeping the time counter for the next “Big Billion Sale” and so on.
The ecommerce website needs to have all these tasks running at the same time to keep the customer engaged with the application and make the website attractive to the user and draw more business.
So, a simple application/website could be a set of multiple tasks running in the background to satisfy the business requirement.
In both illustrations above, we have multiple tasks getting executed at the same time, but there is still a difference between the executions. Let’s look into this further to understand it in more detail.
Understanding Concurrent vs. Parallel Execution
Working with concurrent applications
Let's assume that we have a single-core system, multiple tasks need to be accomplished, but we have a constraint where, at any moment in time, only one task can be executed on the single-core available…
In a concurrent execution model, there is context switching between the tasks. So the application is dealing with multiple tasks but not executing them together as we have only one executing core.
The context switching is so quick between the tasks that it feels like the tasks are running at the same time.
The factor of parallel execution is missing during the execution. As we have a single-core system, we can't have parallel processes running together.
In the illustration above, Concurrency (Without Parallelism), there are two tasks that need to execute concurrently. At any moment in time, only one task is running and there is a constant switch between the tasks.
Adding parallelism to the application
In cases where we are working with single-core, we have resource constraints. If we add more cores to the system, we will have more resources for the application and can enable multiple tasks to execute at the same time on different cores.
In the illustration above, we have two tasks that are running together at any moment in time. The tasks are executing in parallel on different cores.
Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.
While working with Golang, we can scale up the application from concurrent to parallel execution easily. Scalability in Golang can be achieved with ease.
Working With Goroutines
To achieve concurrency and parallelism in Golang, we need to understand the concept of Goroutines. GoRoutines are a Golang wrapper on top of threads and managed by Go runtime rather than the operating system.
Go runtime has the responsibility to assign or withdraw memory resources from Goroutines. A Goroutine is much like a thread to accomplish multiple tasks, but consumes fewer resources than OS threads. Goroutine does not have a one-to-one relationship with threads.
We can divide the application into multiple concurrent tasks. These tasks can be accomplished with different Goroutines. Accomplishing different tasks using multiple Goroutines enables concurrency in the application.
If the application is executing on multiple cores, it also adds parallelism to the application.
The benefits of Goroutines:
- They are lightweight.
- Ability to scale seamlessly.
- They are virtual threads.
- Less memory requirement (2KB).
- Provide additional memory to Goroutines during runtime.
Let's look for a simple Golang program…
The above code executes two self-executing functions inside a Golang main function, sequentially.
We are not using any Goroutine and the program is executed in the same thread. We haven’t added any concurrency to the application. Upon execution, we get the following output:
The above program is executed sequentially, which starts from the main thread, then executes the first self-executing Go function, and then executes the second function and quits after executing the leftover main function body.
We do not have any code executing concurrently in the above code. You can try executing the same in the online editor below:
Introducing Golang Goroutines
In the above scenario, we do not have any Goroutine added to the main function. We can add the Goroutine to the Go program by adding the keyword
go in front of the function execution.
Once we add the
go keyword in front of the self-executing function, we are adding concurrency to the execution. Let's see the impact of adding the
go keyword to the above execution.
The output for the above execution is:
In the above scenario, we are adding the
go keyword to the self-executing functions. The execution starts from the
As soon as it encounters the
go keyword, it creates a separate Goroutine which adds another Go thread to the application. This thread is responsible for executing the function on a separate concurrent thread.
And similarly, another Goroutine will be created once the second
go keyword is encountered, which then executes the self-executing function inside another Goroutine thread.
In this scenario, there would be three threads running in concurrent mode, the
main thread, the thread for the
first self-executing function, and another thread for the second self-executing function. All these threads.
Try executing the same in the online editor below:
Understanding the Difference From Sequential Execution
In the above code, we added a keyword
go in front of the function execution. When the keyword is added to the function execution, it creates a separate Goroutine for the function execution and that function is executed inside a separate Goroutine thread.
In the above execution, adding
go in front of the functions created a separate thread for its execution rather than executing it in the same thread. Hence, adding concurrency and boosting performance.
Adding Parallel Processing
In Go, we can add an execution core with this simple line of code:
This will instruct the application to scale up on multiple cores. Here we have specified that the application can use four cores for the execution.
Once we create Goroutines, they can be executed together in different cores, enabling parallel processing and speeding up the application.
The program will now be allowed to be executed on multiple cores parallelly, ensuring faster executions. The execution for the above code is given below:
When we add
GOMAXPROCS, we are asking the application to scale up the application on multiple cores.
Now, when the
go keywords are added in front of the function execution, they can run separately on different cores and boost up the performance of the application.
Here, we are adding concurrency with parallelism. You can try executing the above program with the below online editor:
Scaling the application to concurrent and parallel mode is very easy when we are working with Golang. Simply append the
go keyword to the function and you can quickly scale up the application execution.
Golang is an easy-to-use and great programming language for parallel and concurrent processing. Thanks for reading.
As part of the Basics, the next concept that we need to discuss is Golang Channels… The article below will provide a further understanding of the next important topic of Golang…
Also, refer to the following documents for details about Golang Execution:
Error Handling in Golang with Panic, Defer and “Recover”
Everything that you wanna know about Golang Error Handling…