Go from Beginner to Expert: A Complete Guide to Learn Golang PART-16 Step-by-Step Guide to understand Concurrency in Golang.

Go from Beginner to Expert: A Complete Guide to Learn Golang PART-16

Step-by-Step Guide to understand Concurrency in Golang.

Md. Faiyaj Zaman
4 min readMar 14, 2023

--

Hello there! Are you interested in learning about concurrency in Golang? If so, you’re in the right place.

In this tutorial, we’ll discuss what concurrency is and why it’s important in modern programming. We’ll also introduce Golang’s goroutines and channels and explain how they work.

Finally, we’ll cover synchronization and mutual exclusion and explain how to implement concurrency in Golang.

Concurrency in Golang

Let’s start with an overview of concurrency in Golang. Concurrency is the ability of a program to perform multiple tasks simultaneously. It allows programs to utilize the processing power of modern machines more efficiently. In Golang, concurrency is achieved through goroutines and channels.

Goroutines

Goroutines are lightweight threads that enable concurrent execution of code. They are created using the “go” keyword, followed by a function call. Let’s take a look at some code:

Goroutine example

func main() {

go doSomething()

fmt.Println("I'm doing something else")

}

func doSomething() {

fmt.Println("I'm doing something")

}

Here, we have a simple function called “doSomething”. We can create a goroutine by calling this function using the “go” keyword. When we run this program, the “doSomething” function will be executed concurrently with the main function.

Channels

Channels are a communication mechanism that allows goroutines to exchange data. They can be used to synchronize the execution of goroutines and ensure that data is accessed safely. Let’s take a look at some code:

Channels Example

func main() {

messages := make(chan string)

go func() {

messages <- "Hello, World!"

}()

fmt.Println(<-messages)

}

Here, we have a channel called “messages” that can transmit strings. We can send data through the channel using the “<-” operator. Similarly, we can receive data from the channel using the same operator. When we run this program, the message “Hello, World!” will be sent through the channel and received by the main function.

Synchronization and Mutual Exclusion

Synchronization and mutual exclusion are important concepts in concurrent programming. Synchronization is the process of coordinating the execution of multiple threads to ensure that they do not interfere with each other. Mutual exclusion is the process of ensuring that only one thread can access a resource at a time. In Golang, these concepts are implemented using mutexes and waitgroups.

Mutex Example

package main

import (

"fmt"

"sync"

)

var (

counter int

lock sync.Mutex

)

func main() {

var wg sync.WaitGroup

for i := 0; i < 1000; i++ {

wg.Add(1)

go incrementCounter(&wg)

}

wg.Wait()

fmt.Println("Counter:", counter)

}

func incrementCounter(wg *sync.WaitGroup) {

defer wg.Done()

lock.Lock()

defer lock.Unlock()

counter++

}

Here, we have a mutex called “lock” that can be used to synchronize access to a shared resource. We can lock and unlock the mutex using the “Lock” and “Unlock” methods, respectively. When a goroutine acquires the lock, all other goroutines that try to acquire the lock will be blocked until the lock is released. This ensures that only one goroutine can access the shared resource at a time.

Implementing Concurrency in Golang

Now that we understand the concepts of concurrency in Golang, let’s talk about how to implement it in our programs. Here are a few tips:

  • Start small: When first adding concurrency to your program, start with small tasks and gradually build up to larger ones.

A small concurrent task example

func main() {

go fmt.Println("Hello, World!")

time.Sleep(1 * time.Second)

}

Here, we have a small task that simply prints “Hello, World!”. We can make this task concurrent by creating a goroutine using the “go” keyword. We can also add a short sleep time to ensure that the program doesn’t exit before the goroutine has a chance to execute.

  • Use channels for communication: When multiple goroutines need to communicate with each other, use channels to ensure safe and synchronized access to shared resources.
func main() {

messages := make(chan string)

go func() {

messages <- "Hello, World!"

}()

fmt.Println(<-messages)

}

Here, we have a small program that uses a channel to transmit a message between two goroutines. This ensures that the message is transmitted safely and that both goroutines are synchronized.

  • Use sync primitives for mutual exclusion: When multiple goroutines need to access a shared resource, use sync primitives like mutexes or wait groups to ensure that only one goroutine can access the resource at a time.
package main

import (
"fmt"
"sync"
)

var (
counter int
lock sync.Mutex
)

func main() {

var wg sync.WaitGroup

for i := 0; i < 1000; i++ {

wg.Add(1)

go incrementCounter(&wg)

}

wg.Wait()

fmt.Println("Counter:", counter)
}

func incrementCounter(wg *sync.WaitGroup) {

lock.Lock()

defer lock.Unlock()

counter++

wg.Done()

}

Here, we have a program that uses a mutex to ensure that only one goroutine can access a shared counter variable at a time. This ensures that the counter is incremented correctly and that there are no race conditions.

And that’s it! You now have a basic understanding of concurrency in Golang.

Remember to start small, use channels for communication, and use sync primitives for mutual exclusion.

With these tips in mind, you’ll be able to write concurrent programs in Golang like a pro!

Don’t forget to Follow and check out the next topic of this series on Golang tools and frameworks.

Happy coding!

--

--