Go – Concurrency

Concurrency is when two or more tasks make progress simultaneously. It could be very tedious to get it right in most of the programming languages. However, Go has a rich support to concurrency and makes the task very easy.

Goroutines:

Goroutines are functions or methods that can run concurrently with other methods. They are light-weight threads. The cost of creating a goroutine is very small compared to that of an actual thread. Hence a GO application commonly has thousands of goroutines running concurrently.

  • Goroutines are managed by GO runtimes
  • go exroutine(a,b) starts a new goroutine that runs exroutine(a,b)
  • Here, the evaluation of exroutine,a,b happens in the current goroutine. The execution happens in the new goroutine.
package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 10; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	go say("is fun")
	say("learning go")
}

Channels:

  • Channels facilitate sending and receiving values. It is achieved using the channel operator ‘<-‘
  • Sending a to channel chch <- a
  • Receive from ch and assign it to aa := <-ch (Data flows in the direction of the arrow)
  • Channels must be created before using it: ch := make(chan int)
  • Send and Receive blocks implicitly until the other side is ready and hence is synchronized.
package main

import "fmt"

func sum(s []int, c chan int){
   sum := 0
   for _,v := range s{
      sum += v
   }
   c <- sum
}

func main(){
   lst := []int{200, 100, 900, 30, 40, 70, 400, 450}
   c := make(chan int)
   go sum(lst[:len(lst)/2],c)
   go sum(lst[len(lst)/2:],c)
   x,y := <-c, <-c
   fmt.Println(x,y,x+y)
}
  • Channels can also be bufferred. The buffer length should be provided as the second argument to makech := make(chan int, 100)
  • A channel can be closed to indicate that no more values can be sent. close(ch)
  • Only the sender should close a channel (not the receiver).
  • Receivers can check if a channel has been closed using the second parameter:       a, ok := <-ch ok is False if the channel is closed.
  • range can be used to keep receiving values until the channel is closed.
for i := range ch {
   fmt.Println(i)
} 
  • Closing a channel is not mandatory. It is only required to send a message to the receiver that no more values are being sent through the channel. It can also be useful to terminate a range loop.

Select

Select lets a goroutine wait on multiple communication operations. It chooses the case that is ready. If multiple are ready, it randomly makes a choice. Default case is chosen if no other case is ready.

select {
	case c <- x:
		x, y = y, x+y
	case <-quit:
		fmt.Println("quit")
		return
	default:
         	fmt.Println("    .")
	}

Mutex:

Mutex or mutual exclusion ensures that only one goroutine can access a variable at a given time to avoid conflicts. Mutex is provided by the sync library which has two methods: Lock and Unlock.

  • A block of code can be executed in mutual exclusion by enclosing it in a call to lock.
  • Defer ensures the mutex is unlocked when required.
package main

import (
	"fmt"
	"sync"
	"time"
)

type mutexUsage struct {
	v   map[string]int
	mx sync.Mutex
}

func (m *mutexUsage) Incr(key string) {
	m.mx.Lock()
	m.v[key]++
	m.mx.Unlock()
}

func (m *mutexUsage) Value(key string) int {
	m.mx.Lock()
	defer m.mx.Unlock()
	return m.v[key]
}

func main() {
	m := mutexUsage{v: make(map[string]int)}
	for i := 0; i < 100; i++ {
		go m.Incr("somekey")
	}

	time.Sleep(time.Second)
	fmt.Println(m.Value("somekey"))
}

Previous Post: Methods and Interfaces

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s