Golang tutorial - 9 Concurrency using goroutine
Tags: go, beginners, tutorial
Published: 2020-07-10 03:14:23

## Concurrency and Parallelism
Concurrency is not a parallelism. Concurrency is a mechanism when two or more tasks can start, and run in overlapped time. Parallelism is a mechanism when many tasks can run at the same time. The illustration of concurrency and parallelism can be seen in the picture below.
![Concurrency illustration](https://dev-to-uploads.s3.amazonaws.com/i/kqejuv1yuv5bbpr30tsf.png)

## Concurrency in Go
There are two main components when creating a concurrent program in Go, there are goroutine and channel. goroutine is a lightweight thread that can be created using the `go` keyword. 

Here is an example of creating goroutine.
```go
func main() {
	fmt.Println("this prints out")
	//create a goroutine
	go odd()
}

//prints out odd numbers
func odd() {
	for i := 1; i < 10; i += 2 {
		fmt.Println(i)
	}
}
```

Output:
```
this prints out
```
Based on that code, notice that the `fmt.Println("this prints out")` is executed but the `odd()` function seems not executed. The `odd()` function is executed in another goroutine. The illustration of that code can be seen in this picture.
![Goroutine illustration](https://dev-to-uploads.s3.amazonaws.com/i/r0r4d8mppauhxy6hy6wn.png)

## Using WaitGroup
WaitGroup is a mechanism that can be used to synchronize the Go code. The basic usages in WaitGroup are:
- `Add()` defines the number of goroutines that are involved.
- `Wait()` defines the wait condition in certain goroutine.
- `Done()` defines the finish condition in certain goroutine. It means that the operation inside the goroutine is finished.	

An example of the usage of WaitGroup can be seen in this code:
```go
package main

import (
	"fmt"
	"sync"
)

//initiate the waitgroup
var wg sync.WaitGroup

func main() {
	//add the 1 goroutine (in this case the odd() function is a goroutine)
	wg.Add(1)
	fmt.Println("this prints out")
	go odd()  //goroutine
	wg.Wait() //wait until odd() function is finished
}

//prints out odd numbers
func odd() {
	for i := 1; i < 10; i += 2 {
		fmt.Println(i)
	}
	//defines that the operation or job inside this function is finished
	wg.Done()
}
```

Output:
```
this prints out
1
3
5
7
9
```
Based on that code, the WaitGroup is used in the `main()` function to synchronize the code so the output from the `odd()` goroutine is visible. 

## Race Condition
The simple definition of race condition is a condition when write and read operations occur in the same variable at the same time.
Race condition makes the value of certain variables inconsistent and introduces bugs in a code. 

In Go, checking the race condition in a code can be done by using the `go run --race` command.

Here is an example of race condition.
```go
package main

import (
	"fmt"
	"runtime"
	"sync"
)

var counter = 0

func main() {
	//declare a number of goroutines
	total := 10

	for i := 0; i < total; i++ {
		//launch a goroutine with anonymous function
		go func() {
			v := counter
			runtime.Gosched() //can be replaced with time.Sleep()
			v++
			counter = v
		}()
	}
	fmt.Println("Counter: ", counter)
}

```

Output (use `go run --race main.go`):
```
Counter:  9
Found 1 data race(s)
exit status 66
```
Based on that code, the race condition occurs when a variable called `v` assigns a value from the `counter` variable then the value of `v` increments by 1 and is assigned again into the `counter` variable. There are many solutions to solve race condition. The solutions are using mutex, atomic, and other alternatives that can be used.

### Using Mutex
Mutex or Mutual Exclusion is a mechanism that can be used to solve a race condition in a code. Mutex is a mechanism that allows only one goroutine to run the critical section (a code with a potential race condition) to prevent race condition. 

Here is an example of using mutex.
```go
package main

import (
	"fmt"
	"runtime"
	"sync"
)

var counter = 0
var wg sync.WaitGroup

//initiate a mutex
var mu sync.Mutex

func main() {
	//declare a number of goroutines
	total := 10

	wg.Add(total)

	for i := 0; i < total; i++ {
		//launch a goroutine with anonymous function
		go func() {
			//lock the operation to available for only one goroutine
			mu.Lock()
			v := counter
			runtime.Gosched()
			v++
			counter = v
			//unlock the operation to available for other next goroutines
			mu.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("Counter: ", counter)
}
```

Output (use `go run --race main.go`):
```
Counter:  10
```
Based on that code, the race condition can be solved by using a mutex. Basically, before entering the critical section the `Lock()` function is called to make sure only one goroutine is available to access the critical section. After entering the critical section, the `Unlock()` function is called to unlock the operation to make sure is available for the next other goroutines.

### Using atomic
There is a package available in the Go programming language that can be used to solve race conditions. The package is called `atomic`. This package implements synchronization algorithms. 

Here is an example of using atomic to solve race condition.
```go
package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

//initiate the counter variable that has a type of int64
var counter int64
var wg sync.WaitGroup

func main() {
	//declare a number of goroutines
	total := 10

	wg.Add(total)

	for i := 0; i < total; i++ {
		//launch a goroutine with anonymous function
		go func() {
			//using atomic package to increment a value in counter variable by 1
			atomic.AddInt64(&counter, 1)
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("Counter: ", counter)
}
```

Output (use `go run --race main.go`):
```
Counter:  10
```
Based on that code, the atomic package can be used to increment a value in the `counter` variable safely. Notice that the `AddInt64()` function takes two arguments. The arguments are a pointer or memory address of a certain variable that the value needs to be added and a certain number to be added into a variable.

## Notes
- [Great resource to learn more about mutex in Go](https://golangbot.com/mutex/)
- [Mutex in Go](https://go.dev/tour/concurrency/9)
- [atomic package documentation](https://pkg.go.dev/sync/atomic)
- [Other example of using atomic package](https://gobyexample.com/atomic-counters)

The concurrency mechanism using channels in Go will be covered in the next blog, I hope this article is helpful to learn the Go programming language. If you have any thoughts or feedback, you can write it in the discussion section below.