Voiced by Amazon Polly |
Introduction
Concurrency is the system’s ability to perform multiple tasks simultaneously. This is a key feature of modern computer systems that allows a program to perform multiple tasks in parallel using multiple CPU cores and resources. Concurrency is critical to building efficient, scalable, and responsive software.
Customized Cloud Solutions to Drive your Business Success
- Cloud Migration
- Devops
- AIML & IoT
Concurrency Architecture(fan-out/fan-in)
The fan-out/fan-in architecture involves splitting a task into multiple subtasks, executing each subtask concurrently, and aggregating the results. This pattern is useful for executing tasks that can be easily parallelized, such as data processing pipelines.
The architecture consists of two main components: the fan-out and fan-in stages.
The fan-out stage involves creating multiple Goroutines to execute the subtasks. The number of Goroutines can be determined based on the number of available cores or subtasks to be executed. Each Goroutine executes a single subtask, and results are passed back to the fan-in stage via a channel.
The fan-in stage involves combining the results into a single result. This is typically done by creating a separate Goroutine to aggregate the results from the fan-out stage. Each result is received from the fan-out stage and added to a data structure (such as a slice or map) until all results have been received. The aggregated result is then returned to the calling code.
Concurrency in Golang
Concurrency is a premium feature of the Go programming language, designed to make writing efficient and responsive concurrent programs easy. In Go, parallelism is achieved through goroutines and channels. The goroutine is a lightweight thread of execution that can run concurrently with other goroutines in the same address space.
Goroutines are created using the “go” keyword and managed by the Go runtime. Because goroutines are lightweight, a single program can create thousands or millions of goroutines without incurring significant overhead. Channels are Go’s primary mechanism for communication and synchronization between goroutines. Channels provide a way for goroutines to send and receive values and can be used to coordinate the execution of concurrent tasks.
Channels are also used to implement select statements. This allows goroutines to wait for multiple channels to become available. Go offers several other concurrency-related features, such as mutexes and condition variables, that can be used to implement more complex synchronization patterns. But for the most part, goroutines and channels provide a simple and effective way to write concurrent programs in Go.
Writing Concurrent programs in Golang
- Goroutines
A goroutine is a lightweight thread of execution that runs concurrently with other goroutines in the same address space. Goroutines are easy to write and maintain and help you write more efficient and scalable programs. To create a goroutine, prefix the function call with the “go” keyword.
1 2 3 4 5 6 7 8 9 10 |
func someFunction() { // Perform some operation } func main() { // Start a Goroutine to execute someFunction() go someFunction() // Perform some other operation in the main Goroutine } |
2. Channels
Channels are a fundamental feature of Go’s concurrency model. They provide a way for goroutines to communicate and synchronize with each other.
Channels can be buffered, sending multiple values without blocking until the buffer is full. Buffered channels are created using the make() function with a second argument specifying the buffer size:
1 |
ch := make(chan string, 10) // create a buffered channel with a capacity of 10 |
3. Mutexes
In Go, a mutex is a synchronization primitive that allows mutual exclusion to be enforced on shared resources. A mutex provides a way to ensure that only one Goroutine can access a shared resource at a time.
The sync package in Go provides the Mutex type, which implements mutexes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup var mu sync.Mutex var sharedResource int // Launch multiple Goroutines to increment the shared resource for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() // Lock the mutex before accessing the shared resource mu.Lock() sharedResource++ // Unlock the mutex after accessing the shared resource mu.Unlock() }() } // Wait for all Goroutines to complete wg.Wait() // Print the final value of the shared resource fmt.Printf("Final value of shared resource: %d\n", sharedResource) } |
Here, we’re using a sync. Mutex to protect access to the sharedResource variable, which is incremented by multiple Goroutines. Each Goroutine must lock the mutex before incrementing the variable and unlock the mutex after accessing the variable. We’re also using a sync.WaitGroup to wait for all Goroutines to complete before printing the final value of the shared resource.
4. WaitGroups
WaitGroup is a synchronization primitive that allows you to wait for a collection of Goroutines to complete their execution before continuing. The sync package in Go provides the WaitGroup type used to implement wait groups.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() fmt.Println("All workers done") } |
The worker function takes an integer id and a pointer to a WaitGroup. The wg.Add(1) method is called to add the Goroutine to the WaitGroup before it executes, and the defer wg.Done() statement signals that the Goroutine has finished executing at the end of the function.
In the main function, we’re creating five worker Goroutines and adding them to the WaitGroup using the wg.Add(1) method. We’re starting each Goroutine using the go keyword. Finally, we’re calling the wg.Wait() method to block until all Goroutines have finished executing.
Sample Concurrent program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package main import ( "fmt" "time" ) func main() { // Create a channel for communication between goroutines c := make(chan int) // Start a goroutine that sends data to the channel go func() { for i := 0; i < 5; i++ { c <- i time.Sleep(time.Second) // Wait for one second before sending the next value } close(c) // Close the channel when done sending data }() // Start a goroutine that receives data from the channel go func() { for num := range c { fmt.Println(num) } }() // Wait for the goroutines to finish time.Sleep(5 * time.Second) } |
In this example, we create channel c for inter-goroutine communication. Then, using anonymous functions, we start two goroutines, one that sends data to the channel and the other that receives data from the channel. When finished, the sender goroutine sends five integers to the channel and closes it. Using a range loop, the receiver goroutine reads data from the channel and prints it to the console. Finally, we wait for the goroutines to complete their use of time Sleep.
This is a simple example, but the same principles can be applied to write more complex concurrent programs in Go.
Conclusion
Concurrency is a fundamental concept in Go, and the language offers powerful mechanisms for developing concurrent programs. You can write efficient and scalable programs that take advantage of multi-core processors by using Goroutines, Channels, WaitGroups, Mutexes, and select statements.
Using these concurrency mechanisms, you can write concurrent programmes that are safe, efficient, and easy to reason about. Because of Go’s emphasis on concurrency, it’s an excellent choice for building scalable, concurrent systems like network servers, distributed systems, and concurrent data processing pipelines.
Get your new hires billable within 1-60 days. Experience our Capability Development Framework today.
- Cloud Training
- Customized Training
- Experiential Learning
About CloudThat
CloudThat is also the official AWS (Amazon Web Services) Advanced Consulting Partner and Training partner and Microsoft gold partner, helping people develop knowledge of the cloud and help their businesses aim for higher goals using best in industry cloud computing practices and expertise. We are on a mission to build a robust cloud computing ecosystem by disseminating knowledge on technological intricacies within the cloud space. Our blogs, webinars, case studies, and white papers enable all the stakeholders in the cloud computing sphere.
Drop a query if you have any questions regarding Go Programming Language and I will get back to you quickly.
To get started, go through our Consultancy page and Managed Services Package that is CloudThat’s offerings.
FAQs
1. How does Go handle concurrency?
ANS: – Go provides goroutines and channels as the main concurrency primitives. Goroutines are lightweight threads that allow concurrent execution, and channels enable communication and synchronization between goroutines.
2. What is a goroutine in Go?
ANS: – A goroutine is a function that can be executed concurrently with other goroutines. Goroutines are lightweight and managed by the Go runtime, allowing efficient creation and scheduling of concurrent tasks.
3. How do I create a goroutine in Go?
ANS: – To create a goroutine, you prefix a function call with the go keyword. For example, go myFunction() starts the execution of myFunction as a goroutine.
WRITTEN BY Bavan M Y
Click to Comment