GoRoutines

  • Used for concurrent execution
  • Not the same as parallel exeuction
  • Uses the go keyword for the line of code to run concurrently
    • Go will not wait for the code to finish executing before moving on
    • Needs to use WaitGroups to ensure that the program does not finish executing before the subtasks has finished
  • Amount of concurrency/parallelism is limited to the number of cores in our CPU

WaitGroup

import(
	"sync"
)
 
var wg = sync.WaitGroup
 
func foo() {
	for i:=0; i < condition; i++ {
		wg.Add(1)
		go bar()
	}
	wg.Wait()
}
 
func bar() {
	wg.Done()
}

Mutex

  • Since concurrent programming means that if we add results to an array, it may not be in order, we should use mutex to control the data
  • Mutual exclusion, methods are lock() and unlock()
  • We can also use a RWMutex{}, which has additional methods RLock() and RUnlock()
import(
	"sync"
)
 
var wg = sync.WaitGroup
var m = sycn.Mutex{}
var results = []string{}
func foo() {
	for i:=0; i < condition; i++ {
		wg.Add(1)
		go bar()
	}
	wg.Wait()
}
 
func bar() {
	m.Lock() // checks if a lock has been already set, if yes, then it will wait here until the lock is released before locking and continuing
	results = append(results, someValue)
	m.Unlock()
	wg.Done()
}

Channels

  • Enables GoRoutines to pass around information
  • Similar to an array, which only has enough room for 1 value

Features

  1. Hold data
  2. Thread safe: avoid data erasure when reading and writing code
  3. Listen for data: can block code until one of these events happen

Making channels

package main
 
func main() {
	var c = make(chan int)
	c <- 1 // adds 1 to the channel
}

Deadlocks

  • Happens if we assign a value from a channel to a variable without GoRoutines, because it blocks code and Go is smart enough to realise that it is an error

Using it with GoRoutines

package main
 
import "fmt"
 
func main() {
	var c = make(chan int)
	go process(c)
	for i:= range c { // for loops works with channels
		fmt.Println(i)
	}
}
 
func process(c chan int) {
	// defer close(c)  also works
	for i := 0; i < 5; i ++ {
		c <- i
	}
	close(c) // prevents deadlock error
}

Storing more values in a channel

  • make(chan int, 5)
  • This allows the main program to continue running other code, since we have more room in a channel and we do not need to pop values out of the channel before printing